quanwei
17 hours ago ad8477d3ee82a3fffd5de4cd60a237c9ee6b1fb7
后台添加供需求发布
14 files modified
4 files added
1719 ■■■■■ changed files
admin/app/api/model/plus/region/Cash.php 2 ●●● patch | view | raw | blame | history
admin/app/api/model/plus/release/Chat.php 34 ●●●● patch | view | raw | blame | history
admin/app/common/model/plus/release/Chat.php 10 ●●●● patch | view | raw | blame | history
admin/app/common/model/plus/release/ChatRelation.php 5 ●●●●● patch | view | raw | blame | history
admin/app/shop/controller/plus/release/DemandProject.php 54 ●●●●● patch | view | raw | blame | history
admin/app/shop/controller/plus/release/SupplyProject.php 56 ●●●●● patch | view | raw | blame | history
admin/app/shop/model/plus/release/DemandProject.php 208 ●●●●● patch | view | raw | blame | history
admin/app/shop/model/plus/release/DemandUser.php 4 ●●●● patch | view | raw | blame | history
admin/app/shop/model/plus/release/SupplyProject.php 234 ●●●●● patch | view | raw | blame | history
admin/app/shop/model/plus/release/SupplyUser.php 2 ●●● patch | view | raw | blame | history
mobile/pages3/release/chat/chat.vue 8 ●●●● patch | view | raw | blame | history
shop_vue/src/api/plus/release.js 24 ●●●●● patch | view | raw | blame | history
shop_vue/src/components/release/GetDemandUser.vue 158 ●●●●● patch | view | raw | blame | history
shop_vue/src/components/release/GetSupplyUser.vue 158 ●●●●● patch | view | raw | blame | history
shop_vue/src/views/plus/release/demandproject/dialog/Add.vue 356 ●●●●● patch | view | raw | blame | history
shop_vue/src/views/plus/release/demandproject/index.vue 24 ●●●●● patch | view | raw | blame | history
shop_vue/src/views/plus/release/supplyproject/dialog/Add.vue 358 ●●●●● patch | view | raw | blame | history
shop_vue/src/views/plus/release/supplyproject/index.vue 24 ●●●●● patch | view | raw | blame | history
admin/app/api/model/plus/region/Cash.php
@@ -1 +1 @@
<?phpnamespace app\api\model\plus\region;use app\common\exception\BaseException;use app\common\model\plus\region\Cash as CashModel;use app\api\model\user\UserAuth;/** * 队长提现明细模型 */class Cash extends CashModel{    /**     * 隐藏字段     */    protected $hidden = [        'update_time',    ];    /**     * 获取队长提现明细     */    public function getList($user_id, $apply_status = -1,$limit=15)    {        $model = $this;        $apply_status > -1 && $model = $model->where('apply_status', '=', $apply_status);        return $model->where('user_id', '=', $user_id)->order(['create_time' => 'desc'])            ->paginate($limit);    }    /**     * 提交申请     */    public function submit($region, $data)    {        // 数据验证        $this->validation($region, $data);        // 新增申请记录        $this->save(array_merge($data, [            'user_id' => $region['user_id'],            'apply_status' => 10,            'app_id' => self::$app_id,        ]));        // 冻结用户资金        $region->freezeMoney($data['money']);        return true;    }    /**     * 数据验证     */    private function validation($region, &$data)    {        // 结算设置        $settlement = Setting::getItem('settlement');        // 最低提现佣金        if ($data['money'] <= 0) {            throw new BaseException(['msg' => '提现金额不正确']);        }        if ($region['money'] <= 0) {            throw new BaseException(['msg' => '当前用户没有可提现佣金']);        }        if ($data['money'] > $region['money']) {            throw new BaseException(['msg' => '提现金额不能大于可提现佣金']);        }        if ($data['money'] < $settlement['min_money']) {            throw new BaseException(['msg' => '最低提现金额为' . $settlement['min_money']]);        }        if (!in_array($data['pay_type'], $settlement['pay_type'])) {            throw new BaseException(['msg' => '提现方式不正确']);        }        if ($data['pay_type'] == '20') {            if (empty($data['alipay_name']) || empty($data['alipay_account'])) {                throw new BaseException(['msg' => '请补全提现信息']);            }        } elseif ($data['pay_type'] == '30') {            if (empty($data['bank_name']) || empty($data['bank_account']) || empty($data['bank_card'])) {                throw new BaseException(['msg' => '请补全提现信息']);            }        } elseif ($data['pay_type'] == '10') {            //微信支付需要实名认证            $auth = UserAuth::detail($region['user_id']);            if(empty($auth)){                throw new BaseException(['msg' => '请先到个人中心->设置->实名认证']);            }elseif(!empty($auth) && $auth["auth_status"] != 1){                throw new BaseException(['msg' => '您的实名认证还未审核通过']);            }        }        // 处理手续费        $data['fee_rate'] = $settlement['fee_rate'];        if ($settlement['fee_rate']) {            $data['fee_money'] = round($data['money'] * $settlement['fee_rate'] / 100, 2);            $data['real_money'] = $data['money'] - $data['fee_money'];        } else {            $data['real_money'] = $data['money'];        }    }}
<?php namespace app\api\model\plus\region; use app\common\exception\BaseException; use app\common\model\plus\region\Cash as CashModel; use app\api\model\user\UserAuth; /**  * 队长提现明细模型  */ class Cash extends CashModel {     /**      * 隐藏字段      */     protected $hidden = [         'update_time',     ];     /**      * 获取队长提现明细      */     public function getList($user_id, $apply_status = -1,$limit=15)     {         $model = $this;         $apply_status > -1 && $model = $model->where('apply_status', '=', $apply_status);         return $model->where('user_id', '=', $user_id)->order(['create_time' => 'desc'])             ->paginate($limit);     }     /**      * 提交申请      */     public function submit($region, $data)     {         // 数据验证         $this->validation($region, $data);         // 新增申请记录         $this->save(array_merge($data, [             'user_id' => $region['user_id'],             'apply_status' => 10,             'app_id' => self::$app_id,         ]));         // 冻结用户资金         $region->freezeMoney($data['money']);         return true;     }     /**      * 数据验证      */     private function validation($region, &$data)     {         // 结算设置         $settlement = Setting::getItem('settlement');         // 最低提现佣金         if ($data['money'] <= 0) {             throw new BaseException(['msg' => '提现金额不正确']);         }         if ($region['money'] <= 0) {             throw new BaseException(['msg' => '当前用户没有可提现佣金']);         }         if ($data['money'] > $region['money']) {             throw new BaseException(['msg' => '提现金额不能大于可提现佣金']);         }         if ($data['money'] < $settlement['min_money']) {             throw new BaseException(['msg' => '最低提现金额为' . $settlement['min_money']]);         }         if (!in_array($data['pay_type'], $settlement['pay_type'])) {             throw new BaseException(['msg' => '提现方式不正确']);         }         if ($data['pay_type'] == '20') {             if (empty($data['alipay_name']) || empty($data['alipay_account'])) {                 throw new BaseException(['msg' => '请补全提现信息']);             }         } elseif ($data['pay_type'] == '30') {             if (empty($data['bank_name']) || empty($data['bank_account']) || empty($data['bank_card'])) {                 throw new BaseException(['msg' => '请补全提现信息']);             }         } elseif ($data['pay_type'] == '10') {             //微信支付需要实名认证             $auth = UserAuth::detail($region['user_id']);             if(empty($auth)){                 throw new BaseException(['msg' => '请先到个人中心->设置->实名认证']);             }elseif(!empty($auth) && $auth["auth_status"] != 1){                 throw new BaseException(['msg' => '您的实名认证还未审核通过']);             }         }         // 处理手续费         $data['fee_rate'] = $settlement['fee_rate'];         if ($settlement['fee_rate']) {             $data['fee_money'] = round($data['money'] * $settlement['fee_rate'] / 100, 2);             $data['real_money'] = $data['money'] - $data['fee_money'];         } else {             $data['real_money'] = $data['money'];         }     } }
admin/app/api/model/plus/release/Chat.php
@@ -24,25 +24,43 @@
    public function myList($user)
    {
        $ChatRelation = new ChatRelation();
        $list = $ChatRelation->with(['user'])
        $list = $ChatRelation
            ->where(['user_id' => $user['user_id']])
            ->whereOr(['other_user_id' => $user['user_id']])
            ->order('update_time desc')
            ->select();
        foreach ($list as $key => &$value) {
            $where['relation_id'] = $value['relation_id'];
            if($value['user_id'] == $user['user_id']){
                $where['user_id']= $other_user_id = $value['other_user_id'];
            }else{
                $where['user_id']= $other_user_id  = $value['user_id'];
        // 过滤掉无效的关系记录(即user_id和other_user_id相同的记录)
        $validList = [];
        foreach ($list as $value) {
            if ($value['user_id'] != $value['other_user_id']) {
                $validList[] = $value;
            }
        }
        foreach ($validList as $key => &$value) {
            $where['relation_id'] = $value['relation_id'];
            // 确定显示的用户ID:应该是与自己不同的另一个用户
            if($value['user_id'] == $user['user_id']){
                // 当前记录是从当前用户角度创建的
                $where['user_id'] = $other_user_id = $value['other_user_id'];
            } else {
                // 当前记录是从对方角度创建的,对方是user_id
                $where['user_id'] = $other_user_id = $value['user_id'];
            }
            // 获取未读消息数量
            $where['status'] = 0;
            $value['num'] = $this->where($where)->count();
            unset($where['status']);
            // 获取最新消息
            $value['newMessage'] = $this->where($where)->order('chat_id desc')->field('content,create_time')->find();
            // 获取对方用户信息
            $value['user'] = UserModel::detail($other_user_id);
        }
        return $list;
        return $validList;
    }
    //获取聊天信息
admin/app/common/model/plus/release/Chat.php
@@ -25,12 +25,18 @@
    //添加信息
    public function add($postdata,$user)
    {
        // 验证:不能给自己发消息
        if (!isset($postdata['you_user_id']) || $postdata['you_user_id'] == $user['user_id']) {
            $this->error = '不能给自己发消息';
            return false;
        }
        // 开启事务
        $this->startTrans();
        try {
            $ChatRelation = new ChatRelation();
            $info = $ChatRelation->where('user_id', '=', $user['user_id'])->where('other_user_id', '=', $postdata['you_user_id'])->find();
            if(empty($info)){
                $info = $ChatRelation->where('user_id', '=', $postdata['you_user_id'])->where('other_user_id', '=', $user['user_id'])->find();
admin/app/common/model/plus/release/ChatRelation.php
@@ -34,6 +34,11 @@
     */
    public static function getRelationId($user_id,$other_user_id)
    {
        // 防止自己与自己创建关系
        if ($user_id == $other_user_id) {
            return 0;
        }
        $model = new static;
        $data = $model->where("user_id",'=',$user_id)->where("other_user_id",'=',$other_user_id)->find();
        if(empty($data)){
admin/app/shop/controller/plus/release/DemandProject.php
@@ -21,6 +21,39 @@
    }
    /**
     * 获取默认数据(分类)
     */
    public function defaultData()
    {
        $model = new ProjectModel;
        $data = $model->getDefaultData();
        return $this->renderSuccess('', compact('data'));
    }
    /**
     * 后台发布需求
     */
    public function add()
    {
        $model = new ProjectModel;
        $data = $this->postData();
        // 获取表单数据
        $formData = json_decode($data['formData'], true);
        // 获取用户ID
        $user_id = isset($data['user_id']) ? intval($data['user_id']) : 0;
        if ($user_id <= 0) {
            return $this->renderError('请选择发布用户');
        }
        // 是否支付连盟币(可选)
        $pay_points = isset($data['pay_points']) ? intval($data['pay_points']) : 0;
        if ($model->addByAdmin($formData, $user_id, $pay_points)) {
            return $this->renderSuccess('发布成功');
        }
        return $this->renderError($model->getError() ?: '发布失败');
    }
    /**
     * 审核
     */
    public function submit($project_id)
@@ -34,6 +67,27 @@
    /**
     * 编辑
     */
    public function edit()
    {
        $model = new ProjectModel;
        $data = $this->postData();
        // 获取表单数据
        $formData = json_decode($data['formData'], true);
        // 获取项目ID
        $project_id = isset($data['project_id']) ? intval($data['project_id']) : 0;
        if ($project_id <= 0) {
            return $this->renderError('项目ID不能为空');
        }
        if ($model->editByAdmin($project_id, $formData)) {
            return $this->renderSuccess('编辑成功');
        }
        return $this->renderError($model->getError() ?: '编辑失败');
    }
    /**
     * 删除
     */
    public function delete($project_id)
admin/app/shop/controller/plus/release/SupplyProject.php
@@ -20,7 +20,40 @@
        return $this->renderSuccess('', compact('list'));
    }
/**
    /**
     * 获取默认数据(分类、标签)
     */
    public function defaultData()
    {
        $model = new ProjectModel;
        $data = $model->getDefaultData();
        return $this->renderSuccess('', compact('data'));
    }
    /**
     * 后台发布供应
     */
    public function add()
    {
        $model = new ProjectModel;
        $data = $this->postData();
        // 获取表单数据
        $formData = json_decode($data['formData'], true);
        // 获取用户ID
        $user_id = isset($data['user_id']) ? intval($data['user_id']) : 0;
        if ($user_id <= 0) {
            return $this->renderError('请选择发布用户');
        }
        // 是否支付连盟币(可选)
        $pay_points = isset($data['pay_points']) ? intval($data['pay_points']) : 0;
        if ($model->addByAdmin($formData, $user_id, $pay_points)) {
            return $this->renderSuccess('发布成功');
        }
        return $this->renderError($model->getError() ?: '发布失败');
    }
    /**
     * 审核
     */
    public function submit($project_id)
@@ -33,6 +66,27 @@
    }
    /**
     * 编辑
     */
    public function edit()
    {
        $model = new ProjectModel;
        $data = $this->postData();
        // 获取表单数据
        $formData = json_decode($data['formData'], true);
        // 获取项目ID
        $project_id = isset($data['project_id']) ? intval($data['project_id']) : 0;
        if ($project_id <= 0) {
            return $this->renderError('项目ID不能为空');
        }
        if ($model->editByAdmin($project_id, $formData)) {
            return $this->renderSuccess('编辑成功');
        }
        return $this->renderError($model->getError() ?: '编辑失败');
    }
    /**
     * 删除
     */
    public function delete($project_id)
admin/app/shop/model/plus/release/DemandProject.php
@@ -4,6 +4,10 @@
use app\common\model\plus\release\Project as ProjectModel;
use app\common\model\plus\release\ReleaseProjectImage as ReleaseProjectImageModel;
use app\common\model\plus\release\ReleaseCategory as ReleaseCategoryModel;
use app\common\model\plus\release\DemandUser as DemandUserModel;
use app\common\model\plus\release\Setting as SettingModel;
use app\common\model\user\PointsLog as PointsLogModel;
/**
 * 模型
@@ -31,7 +35,209 @@
        return $list;
    }
     /**
    /**
     * 获取默认数据(分类、设置)
     */
    public function getDefaultData()
    {
        $setting = SettingModel::getAll();
        $settlement = $setting['settlement']['values'] ?? [];
        return [
            'category_list' => ReleaseCategoryModel::getALL(),
            'demand_price' => $settlement['demand_price'] ?? 0,
        ];
    }
    /**
     * 后台发布需求
     * @param array $data 表单数据
     * @param int $user_id 发布用户ID
     * @param int $pay_points 是否支付连盟币(0不支付,1支付)
     * @return bool
     */
    public function addByAdmin($data, $user_id, $pay_points = 0)
    {
        // 验证必填项
        if (empty($data['name'])) {
            $this->error = '请输入标题';
            return false;
        }
        if (empty($data['category_id'])) {
            $this->error = '请选择分类';
            return false;
        }
        if (empty($data['price'])) {
            $this->error = '请输入您的预算';
            return false;
        }
        if (empty($data['content'])) {
            $this->error = '请输入详细要求';
            return false;
        }
        // 验证需求方用户是否存在
        $demand_user = DemandUserModel::detail($user_id);
        if (!$demand_user || $demand_user['is_delete'] == 1) {
            $this->error = '需求方用户不存在';
            return false;
        }
        // 如果需要支付连盟币,从设置中获取金额并检查余额
        $pay_price = 0;
        if ($pay_points > 0) {
            $setting = SettingModel::getAll();
            $settlement = $setting['settlement']['values'] ?? [];
            $pay_price = $settlement['demand_price'] ?? 0;
            $main_user = \app\common\model\user\User::detail($user_id);
            if ($main_user['points'] < $pay_price) {
                $this->error = '用户连盟币不足';
                return false;
            }
        }
        $save_data = [
            'user_id' => $user_id,
            'name' => $data['name'],
            'category_id' => $data['category_id'],
            'price' => $data['price'],
            'content' => $data['content'],
            'product_content' => $data['product_content'] ?? '',
            'product_case' => $data['product_case'] ?? '',
            'detail' => $data['detail'] ?? '',
            'project_type' => 0, // 需求类型
            'show_phone' => $data['show_phone'] ?? 1,
            'is_show' => $data['is_show'] ?? 1,
            'status' => 1, // 后台发布自动通过审核
            'app_id' => self::$app_id,
        ];
        if (!empty($data['finish_time']) && $data['finish_time'] != '请选择日期') {
            $save_data['finish_time'] = strtotime($data['finish_time']);
        } else {
            $save_data['finish_time'] = 0;
        }
        return $this->transaction(function () use ($data, $save_data, $pay_points, $user_id) {
            // 记录内容
            $this->save($save_data);
            // 记录图片
            $this->saveAllImages($this['project_id'], $data);
            // 如果需要支付连盟币
            if ($pay_points > 0) {
                // 从设置中获取支付金额
                $setting = SettingModel::getAll();
                $settlement = $setting['settlement']['values'] ?? [];
                $pay_price = $settlement['demand_price'] ?? 0;
                // 减少主用户表的连盟币
                \app\common\model\user\User::where('user_id', '=', $user_id)->dec('points', $pay_price)->update();
                PointsLogModel::add([
                    'user_id' => $user_id,
                    'value' => -$pay_price,
                    'describe' => "后台发布需求消耗连盟币",
                    'app_id' => self::$app_id,
                ]);
            }
            return $this['project_id'];
        });
    }
    /**
     * 后台编辑需求
     * @param int $project_id 项目ID
     * @param array $data 表单数据
     * @return bool
     */
    public function editByAdmin($project_id, $data)
    {
        // 验证必填项
        if (empty($data['name'])) {
            $this->error = '请输入标题';
            return false;
        }
        if (empty($data['category_id'])) {
            $this->error = '请选择分类';
            return false;
        }
        if (empty($data['price'])) {
            $this->error = '请输入您的预算';
            return false;
        }
        if (empty($data['content'])) {
            $this->error = '请输入详细要求';
            return false;
        }
        $model = self::detail($project_id);
        if (!$model) {
            $this->error = '项目不存在';
            return false;
        }
        $save_data = [
            'name' => $data['name'],
            'category_id' => $data['category_id'],
            'price' => $data['price'],
            'content' => $data['content'],
            'product_content' => $data['product_content'] ?? '',
            'product_case' => $data['product_case'] ?? '',
            'detail' => $data['detail'] ?? '',
            'show_phone' => $data['show_phone'] ?? 1,
            'is_show' => $data['is_show'] ?? 1,
        ];
        if (!empty($data['finish_time']) && $data['finish_time'] != '请选择日期') {
            $save_data['finish_time'] = strtotime($data['finish_time']);
        } else {
            $save_data['finish_time'] = 0;
        }
        return $model->transaction(function () use ($model, $project_id, $data, $save_data) {
            // 更新内容
            $model->save($save_data);
            // 更新图片
            $model->saveAllImages($project_id, $data);
            return true;
        });
    }
    /**
     * 记录图片
     */
    private function saveAllImages($id, $formData)
    {
        (new ReleaseProjectImageModel())->where("project_id", "=", $id)->delete();
        // 生成图片数据
        if (!empty($formData['image_list']) && is_array($formData['image_list'])) {
            $imageData = [];
            foreach ($formData['image_list'] as $imageItem) {
                // 处理图片ID:可能是对象数组或纯ID
                $image_id = 0;
                if (is_array($imageItem)) {
                    $image_id = !empty($imageItem['image_id']) ? $imageItem['image_id'] : ($imageItem['file_id'] ?? 0);
                } elseif (is_numeric($imageItem)) {
                    $image_id = $imageItem;
                }
                if ($image_id > 0) {
                    $imageData[] = [
                        'project_id' => $id,
                        'image_id' => $image_id,
                        'app_id' => self::$app_id
                    ];
                }
            }
            if (!empty($imageData)) {
                $model = new ReleaseProjectImageModel;
                return $model->saveAll($imageData);
            }
        }
        return true;
    }
    /**
     * 审核
     */
    public function submit($param)
admin/app/shop/model/plus/release/DemandUser.php
@@ -19,8 +19,8 @@
    public function getList($data)
    {
        // 构建查询规则
        $model = $this->alias('du')->with('grade')
            ->field('du.*, user.nickName, user.avatarUrl')
        $model = $this->alias('du')->with(['grade'])
            ->field('du.*, user.nickName, user.avatarUrl, user.points')
            ->join('user', 'user.user_id = du.user_id')
            ->where('du.is_delete', '=', 0)
            ->order(['du.create_time' => 'desc']);
admin/app/shop/model/plus/release/SupplyProject.php
@@ -5,6 +5,11 @@
use app\common\model\plus\release\Project as ProjectModel;
use app\common\model\plus\release\ReleaseProjectImage as ReleaseProjectImageModel;
use app\common\model\plus\release\ReleaseProjectTag as ReleaseProjectTagModel;
use app\common\model\plus\release\ReleaseCategory as ReleaseCategoryModel;
use app\common\model\plus\release\Tag as TagModel;
use app\common\model\plus\release\SupplyUser as SupplyUserModel;
use app\common\model\plus\release\Setting as SettingModel;
use app\common\model\user\PointsLog as PointsLogModel;
/**
 * 模型
@@ -36,6 +41,235 @@
    }
    /**
     * 获取默认数据(分类、标签、设置)
     */
    public function getDefaultData()
    {
        $setting = SettingModel::getAll();
        $settlement = $setting['settlement']['values'] ?? [];
        return [
            'category_list' => ReleaseCategoryModel::getALL(),
            'tag_list' => TagModel::getALL(),
            'supply_price' => $settlement['supply_price'] ?? 0,
        ];
    }
    /**
     * 后台发布供应
     * @param array $data 表单数据
     * @param int $user_id 发布用户ID
     * @param int $pay_points 是否支付连盟币(0不支付,1支付)
     * @return bool
     */
    public function addByAdmin($data, $user_id, $pay_points = 0)
    {
        // 验证必填项
        if (empty($data['name'])) {
            $this->error = '请输入标题';
            return false;
        }
        if (empty($data['category_id'])) {
            $this->error = '请选择分类';
            return false;
        }
        if (empty($data['price'])) {
            $this->error = '请输入您的价格';
            return false;
        }
        if (empty($data['content'])) {
            $this->error = '请输入详细描述';
            return false;
        }
        // 验证供应方用户是否存在
        $supply_user = SupplyUserModel::detail($user_id);
        if (!$supply_user || $supply_user['is_delete'] == 1) {
            $this->error = '供应方用户不存在';
            return false;
        }
        // 如果需要支付连盟币,从设置中获取金额并检查余额
        $pay_price = 0;
        if ($pay_points > 0) {
            $setting = SettingModel::getAll();
            $settlement = $setting['settlement']['values'] ?? [];
            $pay_price = $settlement['supply_price'] ?? 0;
            $main_user = \app\common\model\user\User::detail($user_id);
            if ($main_user['points'] < $pay_price) {
                $this->error = '用户连盟币不足';
                return false;
            }
        }
        $save_data = [
            'user_id' => $user_id,
            'name' => $data['name'],
            'category_id' => $data['category_id'],
            'price' => $data['price'],
            'content' => $data['content'],
            'product_content' => $data['product_content'] ?? '',
            'product_case' => $data['product_case'] ?? '',
            'detail' => $data['detail'] ?? '',
            'project_type' => 1, // 供应类型
            'is_show' => $data['is_show'] ?? 1,
            'status' => 1, // 后台发布自动通过审核
            'app_id' => self::$app_id,
        ];
        if (!empty($data['finish_time']) && $data['finish_time'] != '请选择日期') {
            $save_data['finish_time'] = strtotime($data['finish_time']);
        } else {
            $save_data['finish_time'] = 0;
        }
        return $this->transaction(function () use ($data, $save_data, $pay_points, $user_id) {
            // 记录内容
            $this->save($save_data);
            // 记录图片
            $this->saveAllImages($this['project_id'], $data);
            // 记录标签
            $this->saveTag($this['project_id'], $data);
            // 如果需要支付连盟币
            if ($pay_points > 0) {
                // 从设置中获取支付金额
                $setting = SettingModel::getAll();
                $settlement = $setting['settlement']['values'] ?? [];
                $pay_price = $settlement['supply_price'] ?? 0;
                // 减少主用户表的连盟币
                \app\common\model\user\User::where('user_id', '=', $user_id)->dec('points', $pay_price)->update();
                PointsLogModel::add([
                    'user_id' => $user_id,
                    'value' => -$pay_price,
                    'describe' => "后台发布供应消耗连盟币",
                    'app_id' => self::$app_id,
                ]);
            }
            return $this['project_id'];
        });
    }
    /**
     * 后台编辑供应
     * @param int $project_id 项目ID
     * @param array $data 表单数据
     * @return bool
     */
    public function editByAdmin($project_id, $data)
    {
        // 验证必填项
        if (empty($data['name'])) {
            $this->error = '请输入标题';
            return false;
        }
        if (empty($data['category_id'])) {
            $this->error = '请选择分类';
            return false;
        }
        if (empty($data['price'])) {
            $this->error = '请输入您的价格';
            return false;
        }
        if (empty($data['content'])) {
            $this->error = '请输入详细描述';
            return false;
        }
        $model = self::detail($project_id);
        if (!$model) {
            $this->error = '项目不存在';
            return false;
        }
        $save_data = [
            'name' => $data['name'],
            'category_id' => $data['category_id'],
            'price' => $data['price'],
            'content' => $data['content'],
            'product_content' => $data['product_content'] ?? '',
            'product_case' => $data['product_case'] ?? '',
            'detail' => $data['detail'] ?? '',
            'is_show' => $data['is_show'] ?? 1,
        ];
        if (!empty($data['finish_time']) && $data['finish_time'] != '请选择日期') {
            $save_data['finish_time'] = strtotime($data['finish_time']);
        } else {
            $save_data['finish_time'] = 0;
        }
        return $model->transaction(function () use ($model, $project_id, $data, $save_data) {
            // 更新内容
            $model->save($save_data);
            // 更新图片
            $model->saveAllImages($project_id, $data);
            // 更新标签
            $model->saveTag($project_id, $data);
            return true;
        });
    }
    /**
     * 记录图片
     */
    private function saveAllImages($id, $formData)
    {
        (new ReleaseProjectImageModel())->where("project_id", "=", $id)->delete();
        // 生成图片数据
        if (!empty($formData['image_list']) && is_array($formData['image_list'])) {
            $imageData = [];
            foreach ($formData['image_list'] as $imageItem) {
                // 处理图片ID:可能是对象数组或纯ID
                $image_id = 0;
                if (is_array($imageItem)) {
                    $image_id = !empty($imageItem['image_id']) ? $imageItem['image_id'] : ($imageItem['file_id'] ?? 0);
                } elseif (is_numeric($imageItem)) {
                    $image_id = $imageItem;
                }
                if ($image_id > 0) {
                    $imageData[] = [
                        'project_id' => $id,
                        'image_id' => $image_id,
                        'app_id' => self::$app_id
                    ];
                }
            }
            if (!empty($imageData)) {
                $model = new ReleaseProjectImageModel;
                return $model->saveAll($imageData);
            }
        }
        return true;
    }
    /**
     * 记录标签
     */
    private function saveTag($id, $formData)
    {
        (new ReleaseProjectTagModel())->where("project_id", "=", $id)->delete();
        // 生成数据
        if (!empty($formData['tag_list'])) {
            $tagData = [];
            foreach ($formData['tag_list'] as $val) {
                $tagData[] = [
                    'project_id' => $id,
                    'tag_id' => $val,
                    'app_id' => self::$app_id
                ];
            }
            $model = new ReleaseProjectTagModel;
            return !empty($tagData) && $model->saveAll($tagData);
        }
        return true;
    }
    /**
     * 审核
     */
    public function submit($param)
admin/app/shop/model/plus/release/SupplyUser.php
@@ -20,7 +20,7 @@
    {
        // 构建查询规则
        $model = $this->alias('su')->with('grade')
            ->field('su.*, user.nickName, user.avatarUrl')
            ->field('su.*, user.nickName, user.avatarUrl, user.points')
            ->join('user', 'user.user_id = su.user_id')
            ->where('su.is_delete', '=', 0)
            ->order(['su.create_time' => 'desc']);
mobile/pages3/release/chat/chat.vue
@@ -5,7 +5,7 @@
            <view class="im_interface_content" ref='container'>
                <view :class="item.user_id == user_id?'im_text':'im_text2'" class="m-item" v-for="(item,index) in content_list" :key="index">
                    <image class="avatar" :src="item.user_id != user_id?item.user.avatarUrl:avatarUrl"
                    <image class="avatar" :src="item.user_id == user_id?myavatarUrl:(item.user&&item.user.avatarUrl?item.user.avatarUrl:otherAvatarUrl)"
                     mode=""></image>
                    <view>
                        <view class="my_date">{{item.create_time}}</view>
@@ -38,8 +38,8 @@
            return {
                user_id: '', //我的user_id
                you_user_id: '', //对方的suser_id
                myavatarUrl: '',
                avatarUrl: '',
                myavatarUrl: '', //我的头像
                otherAvatarUrl: '', //对方的头像
                phoneHeight: 0,
                /*可滚动视图区域高度*/
                scrollviewHigh: 0,
@@ -76,7 +76,7 @@
            let self = this;
            self.you_user_id = option.you_user_id;
            self.nickName = option.nickname;
            self.avatarUrl = option.avatarurl;
            self.otherAvatarUrl = option.avatarurl; // 对方的头像
            uni.setNavigationBarTitle({
                title: self.nickName
            })
shop_vue/src/api/plus/release.js
@@ -90,6 +90,18 @@
    demandEdit(data, errorback) {
        return request._post('/shop/plus.release.DemandProject/edit', data, errorback);
    },
    /*需求发布默认数据*/
    demandProjectDefaultData(data, errorback) {
        return request._post('/shop/plus.release.DemandProject/defaultData', data, errorback);
    },
    /*后台发布需求*/
    demandProjectAdd(data, errorback) {
        return request._post('/shop/plus.release.DemandProject/add', data, errorback);
    },
    /*后台编辑需求*/
    demandProjectEdit(data, errorback) {
        return request._post('/shop/plus.release.DemandProject/edit', data, errorback);
    },
    /*供应发布列表*/
    supplyProject(data, errorback) {
        return request._post('/shop/plus.release.supplyProject/index', data, errorback);
@@ -106,6 +118,18 @@
    supplyEdit(data, errorback) {
        return request._post('/shop/plus.release.supplyProject/edit', data, errorback);
    },
    /*供应发布默认数据*/
    supplyProjectDefaultData(data, errorback) {
        return request._post('/shop/plus.release.supplyProject/defaultData', data, errorback);
    },
    /*后台发布供应*/
    supplyProjectAdd(data, errorback) {
        return request._post('/shop/plus.release.supplyProject/add', data, errorback);
    },
    /*后台编辑供应*/
    supplyProjectEdit(data, errorback) {
        return request._post('/shop/plus.release.supplyProject/edit', data, errorback);
    },
    /*订单列表*/
    releaseOrder(data, errorback) {
        return request._post('/shop/plus.release.order/index', data, errorback);
shop_vue/src/components/release/GetDemandUser.vue
New file
@@ -0,0 +1,158 @@
<template>
  <el-dialog title="选择需求方用户" :visible.sync="dialogVisible" @close="cancelFunc" :close-on-click-modal="false" :close-on-press-escape="false" width="800px" append-to-body>
    <div class="common-seach-wrap">
      <el-form size="small" :inline="true" :model="formInline" class="demo-form-inline">
        <el-form-item label="等级">
          <el-select v-model="formInline.grade_id" placeholder="请选择等级" style="width: 120px;">
            <el-option label="全部" value="0"></el-option>
            <el-option v-for="(item, index) in gradeList" :key="index" :label="item.name" :value="item.grade_id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="用户"><el-input placeholder="请输入昵称|姓名|手机号" v-model="formInline.nick_name"></el-input></el-form-item>
        <el-form-item>
          <el-button icon="el-icon-search" @click="search">查询</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="product-content">
      <div class="table-wrap">
        <el-table :data="tableData" size="small" border style="width: 100%" v-loading="loading">
          <el-table-column prop="" label="头像" width="70">
            <template slot-scope="scope">
              <img :src="scope.row.avatarUrl" class="radius" width="30" height="30" />
            </template>
          </el-table-column>
          <el-table-column prop="nickName" label="昵称"></el-table-column>
          <el-table-column prop="real_name" label="姓名"></el-table-column>
          <el-table-column prop="mobile" label="手机号"></el-table-column>
          <el-table-column prop="grade.name" label="等级"></el-table-column>
          <el-table-column label="操作" width="80">
            <template slot-scope="scope">
              <span class="green" v-if="selectedUser && selectedUser.user_id==scope.row.user_id" size="mini">已选择</span>
              <el-button v-else type="text" size="mini" @click="selectUser(scope.row)">选择</el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <div class="pagination">
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          background
          :current-page="curPage"
          :page-sizes="[10, 20, 50, 100]"
          :page-size="pageSize"
          layout="total, prev, pager, next, jumper"
          :total="totalDataNumber"
        ></el-pagination>
      </div>
    </div>
    <div slot="footer" class="dialog-footer">
      <el-button size="small" @click="dialogVisible = false">取 消</el-button>
      <el-button size="small" type="primary" @click="confirmFunc">确 定</el-button>
    </div>
  </el-dialog>
</template>
<script>
import PlusApi from '@/api/plus/release.js';
export default {
  data() {
    return {
      loading: true,
      curPage: 1,
      pageSize: 15,
      totalDataNumber: 0,
      formInline: {
        grade_id: '',
        nick_name: ''
      },
      gradeList: [],
      tableData: [],
      selectedUser: null,
      dialogVisible: false
    };
  },
  props: {
    is_open: Boolean
  },
  watch: {
    is_open: function(n, o) {
      if (n != o) {
        this.dialogVisible = n;
        if (n) {
          this.getTableList();
        }
      }
    }
  },
  created() {},
  methods: {
    handleCurrentChange(val) {
      this.curPage = val;
      this.getTableList();
    },
    handleSizeChange(val) {
      this.curPage = 1;
      this.pageSize = val;
      this.getTableList();
    },
    getTableList() {
      let self = this;
      self.loading = true;
      let params = self.formInline;
      params.page = self.curPage;
      params.list_rows = self.pageSize;
      PlusApi.demandUserList(params, true).then(data => {
          self.loading = false;
          self.tableData = data.data.list.data;
          self.totalDataNumber = data.data.list.total;
        })
        .catch(error => {
          self.loading = false;
        });
    },
    search() {
      this.curPage = 1;
      this.getTableList();
    },
    confirmFunc() {
      if (!this.selectedUser) {
        this.$message.warning('请选择用户');
        return;
      }
      this.emitFunc(this.selectedUser);
    },
    cancelFunc() {
      this.emitFunc();
    },
    emitFunc(user) {
      this.dialogVisible = false;
      if (user) {
        this.$emit('close', {
          type: 'success',
          params: user
        });
      } else {
        this.$emit('close', {
          type: 'error'
        });
      }
    },
    selectUser(user) {
      this.selectedUser = user;
      this.confirmFunc();
    }
  }
};
</script>
shop_vue/src/components/release/GetSupplyUser.vue
New file
@@ -0,0 +1,158 @@
<template>
  <el-dialog title="选择供应方用户" :visible.sync="dialogVisible" @close="cancelFunc" :close-on-click-modal="false" :close-on-press-escape="false" width="800px" append-to-body>
    <div class="common-seach-wrap">
      <el-form size="small" :inline="true" :model="formInline" class="demo-form-inline">
        <el-form-item label="等级">
          <el-select v-model="formInline.grade_id" placeholder="请选择等级" style="width: 120px;">
            <el-option label="全部" value="0"></el-option>
            <el-option v-for="(item, index) in gradeList" :key="index" :label="item.name" :value="item.grade_id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="用户"><el-input placeholder="请输入昵称|姓名|手机号" v-model="formInline.nick_name"></el-input></el-form-item>
        <el-form-item>
          <el-button icon="el-icon-search" @click="search">查询</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="product-content">
      <div class="table-wrap">
        <el-table :data="tableData" size="small" border style="width: 100%" v-loading="loading">
          <el-table-column prop="" label="头像" width="70">
            <template slot-scope="scope">
              <img :src="scope.row.avatarUrl" class="radius" width="30" height="30" />
            </template>
          </el-table-column>
          <el-table-column prop="nickName" label="昵称"></el-table-column>
          <el-table-column prop="real_name" label="姓名"></el-table-column>
          <el-table-column prop="mobile" label="手机号"></el-table-column>
          <el-table-column prop="grade.name" label="等级"></el-table-column>
          <el-table-column label="操作" width="80">
            <template slot-scope="scope">
              <span class="green" v-if="selectedUser && selectedUser.user_id==scope.row.user_id" size="mini">已选择</span>
              <el-button v-else type="text" size="mini" @click="selectUser(scope.row)">选择</el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <div class="pagination">
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          background
          :current-page="curPage"
          :page-sizes="[10, 20, 50, 100]"
          :page-size="pageSize"
          layout="total, prev, pager, next, jumper"
          :total="totalDataNumber"
        ></el-pagination>
      </div>
    </div>
    <div slot="footer" class="dialog-footer">
      <el-button size="small" @click="dialogVisible = false">取 消</el-button>
      <el-button size="small" type="primary" @click="confirmFunc">确 定</el-button>
    </div>
  </el-dialog>
</template>
<script>
import PlusApi from '@/api/plus/release.js';
export default {
  data() {
    return {
      loading: true,
      curPage: 1,
      pageSize: 15,
      totalDataNumber: 0,
      formInline: {
        grade_id: '',
        nick_name: ''
      },
      gradeList: [],
      tableData: [],
      selectedUser: null,
      dialogVisible: false
    };
  },
  props: {
    is_open: Boolean
  },
  watch: {
    is_open: function(n, o) {
      if (n != o) {
        this.dialogVisible = n;
        if (n) {
          this.getTableList();
        }
      }
    }
  },
  created() {},
  methods: {
    handleCurrentChange(val) {
      this.curPage = val;
      this.getTableList();
    },
    handleSizeChange(val) {
      this.curPage = 1;
      this.pageSize = val;
      this.getTableList();
    },
    getTableList() {
      let self = this;
      self.loading = true;
      let params = self.formInline;
      params.page = self.curPage;
      params.list_rows = self.pageSize;
      PlusApi.supplyUserList(params, true).then(data => {
          self.loading = false;
          self.tableData = data.data.list.data;
          self.totalDataNumber = data.data.list.total;
        })
        .catch(error => {
          self.loading = false;
        });
    },
    search() {
      this.curPage = 1;
      this.getTableList();
    },
    confirmFunc() {
      if (!this.selectedUser) {
        this.$message.warning('请选择用户');
        return;
      }
      this.emitFunc(this.selectedUser);
    },
    cancelFunc() {
      this.emitFunc();
    },
    emitFunc(user) {
      this.dialogVisible = false;
      if (user) {
        this.$emit('close', {
          type: 'success',
          params: user
        });
      } else {
        this.$emit('close', {
          type: 'error'
        });
      }
    },
    selectUser(user) {
      this.selectedUser = user;
      this.confirmFunc();
    }
  }
};
</script>
shop_vue/src/views/plus/release/demandproject/dialog/Add.vue
New file
@@ -0,0 +1,356 @@
<template>
  <!--
      作者:yj
      后台发布需求
    -->
  <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" @close="dialogFormVisible" :close-on-click-modal="false" :close-on-press-escape="false" width="800px">
    <el-form size="small" :model="form" ref="form" :rules="rules" label-width="120px">
      <el-form-item label="发布用户" prop="user_id">
        <el-input v-model="selectedUserName" placeholder="请选择用户" disabled style="width: 200px;"></el-input>
        <el-button type="primary" @click="openUserSelect">选择用户</el-button>
        <span v-if="selectedUser" class="ml10 gray9">连盟币: {{ selectedUser.points }}</span>
      </el-form-item>
      <el-form-item label="标题" prop="name">
        <el-input v-model="form.name" placeholder="请输入标题" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="分类" prop="category_id">
        <el-select v-model="form.category_id" placeholder="请选择分类" style="width: 200px;">
          <el-option v-for="item in categoryList" :key="item.category_id" :label="item.name" :value="item.category_id"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="预算(元)" prop="price">
        <el-input v-model="form.price" placeholder="请输入预算" style="width: 200px;"></el-input>
      </el-form-item>
      <el-form-item label="详细要求" prop="content">
        <el-input type="textarea" :rows="3" v-model="form.content" placeholder="请输入详细要求" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="图片">
        <div class="upload-list d-s-c">
          <div class="item" v-for="(imgs, img_num) in form.image_list" :key="img_num">
            <img :src="imgs.file_path" style="width: 60px; height: 60px; margin-right: 10px;" />
            <el-button type="text" size="mini" @click="deleteImg(img_num)">删除</el-button>
          </div>
          <div class="item img-select" @click="openUpload"><i class="el-icon-plus"></i></div>
        </div>
      </el-form-item>
      <el-form-item label="期望完成时间">
        <el-date-picker v-model="form.finish_time" type="date" placeholder="选择日期" value-format="yyyy-MM-dd" style="width: 200px;"></el-date-picker>
      </el-form-item>
      <el-form-item label="期望地点">
        <el-input v-model="form.detail" placeholder="请输入期望地点" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="产品介绍">
        <el-input type="textarea" :rows="3" v-model="form.product_content" placeholder="请输入产品介绍" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="产品应用案例">
        <el-input type="textarea" :rows="3" v-model="form.product_case" placeholder="请输入产品应用案例" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="公开联系方式">
        <el-radio-group v-model="form.show_phone">
          <el-radio :label="1">是</el-radio>
          <el-radio :label="0">否</el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="是否展示">
        <el-radio-group v-model="form.is_show">
          <el-radio :label="1">是</el-radio>
          <el-radio :label="0">否</el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="支付连盟币" v-if="!isEdit">
        <el-radio-group v-model="payPoints">
          <el-radio :label="0">不支付</el-radio>
          <el-radio :label="1">支付 ({{ demandPrice }} 连盟币)</el-radio>
        </el-radio-group>
      </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
      <el-button @click="dialogFormVisible">取 消</el-button>
      <el-button type="primary" @click="submitForm" :loading="loading">确 定</el-button>
    </div>
    <!-- 需求方用户选择器 -->
    <GetDemandUser :is_open="showUserSelector" @close="handleUserSelect"></GetDemandUser>
    <!-- 图片上传组件 -->
    <Upload v-if="isUpload" :config="uploadConfig" :isupload="isUpload" @returnImgs="returnImgsFunc">上传图片</Upload>
  </el-dialog>
</template>
<script>
import PlusApi from '@/api/plus/release.js';
import GetDemandUser from '@/components/release/GetDemandUser.vue';
import Upload from '@/components/file/Upload';
import { deepClone } from '@/utils/base.js';
export default {
  components: {
    GetDemandUser,
    Upload
  },
  data() {
    return {
      /*是否显示*/
      dialogVisible: false,
      loading: false,
      formLabelWidth: '120px',
      /*是否编辑模式*/
      isEdit: false,
      /*编辑的项目ID*/
      editProjectId: null,
      /*表单数据*/
      form: {
        user_id: '',
        name: '',
        category_id: '',
        price: '',
        content: '',
        image_list: [],
        finish_time: '',
        detail: '',
        product_content: '',
        product_case: '',
        show_phone: 1,
        is_show: 1
      },
      /*验证规则*/
      rules: {
        user_id: [{ required: true, message: '请选择发布用户', trigger: 'change' }],
        name: [{ required: true, message: '请输入标题', trigger: 'blur' }],
        category_id: [{ required: true, message: '请选择分类', trigger: 'change' }],
        price: [{ required: true, message: '请输入预算', trigger: 'blur' }],
        content: [{ required: true, message: '请输入详细要求', trigger: 'blur' }]
      },
      /*分类列表*/
      categoryList: [],
      /*选中的用户*/
      selectedUser: null,
      selectedUserName: '',
      /*是否显示用户选择器*/
      showUserSelector: false,
      /*是否支付连盟币*/
      payPoints: 0,
      /*支付连盟币金额(从设置获取)*/
      demandPrice: 0,
      /*是否显示上传组件*/
      isUpload: false,
      /*上传配置*/
      uploadConfig: {
        total: 9,
        file_type: 'image'
      }
    };
  },
  props: ['open_add', 'edit_data'],
  created() {
    this.dialogVisible = this.open_add;
    this.getDefaultData();
    // 判断是否为编辑模式
    if (this.edit_data && this.edit_data.project_id) {
      this.isEdit = true;
      this.editProjectId = this.edit_data.project_id;
      this.initEditData();
    }
  },
  methods: {
    /*获取默认数据*/
    getDefaultData() {
      let self = this;
      PlusApi.demandProjectDefaultData({}, true).then(data => {
        self.categoryList = data.data.data.category_list || [];
        self.demandPrice = data.data.data.demand_price || 0;
      }).catch(error => {});
    },
    /*初始化编辑数据*/
    initEditData() {
      const data = this.edit_data;
      this.selectedUser = {
        user_id: data.user_id,
        nickName: data.nickName || (data.user && data.user.nickName),
        balance: data.balance || (data.user && data.user.balance),
        points: data.points || (data.user && data.user.points)
      };
      this.selectedUserName = this.selectedUser.nickName || ('用户ID:' + data.user_id);
      this.form.user_id = data.user_id;
      this.form.name = data.name;
      this.form.category_id = data.category_id;
      this.form.price = data.price;
      this.form.content = data.content;
      this.form.image_list = data.image_list || [];
      this.form.finish_time = data.finish_time || '';
      this.form.detail = data.detail || '';
      this.form.product_content = data.product_content || '';
      this.form.product_case = data.product_case || '';
      this.form.show_phone = data.show_phone;
      this.form.is_show = data.is_show;
    },
    /*打开用户选择器*/
    openUserSelect() {
      this.showUserSelector = true;
    },
    /*打开上传组件*/
    openUpload() {
      this.isUpload = true;
    },
    /*处理用户选择*/
    handleUserSelect(result) {
      this.showUserSelector = false;
      if (result && result.type === 'success' && result.params) {
        const user = result.params;
        this.selectedUser = user;
        this.selectedUserName = user.nickName || user.real_name || ('用户ID:' + user.user_id);
        this.form.user_id = user.user_id;
      }
    },
    /*处理上传图片返回*/
    returnImgsFunc(e) {
      if (e != null) {
        this.form.image_list = this.form.image_list.concat(e);
      }
      this.isUpload = false;
    },
    /*删除图片*/
    deleteImg(index) {
      this.form.image_list.splice(index, 1);
    },
    /*提交表单*/
    submitForm() {
      let self = this;
      self.$refs.form.validate(valid => {
        if (valid) {
          if (!self.selectedUser || !self.selectedUser.user_id) {
            self.$message.error('请选择发布用户');
            return;
          }
          self.loading = true;
          let params = {
            user_id: self.selectedUser.user_id,
            formData: JSON.stringify(self.form)
          };
          if (self.isEdit) {
            // 编辑模式
            params.project_id = self.editProjectId;
            PlusApi.demandProjectEdit(params, true).then(data => {
              self.loading = false;
              self.$message.success('编辑成功');
              self.$emit('closeDialog', {
                type: 'success',
                openDialog: false
              });
            }).catch(error => {
              self.loading = false;
            });
          } else {
            // 新增模式
            params.pay_points = self.payPoints;
            PlusApi.demandProjectAdd(params, true).then(data => {
              self.loading = false;
              self.$message.success('发布成功');
              self.$emit('closeDialog', {
                type: 'success',
                openDialog: false
              });
            }).catch(error => {
              self.loading = false;
            });
          }
        }
      });
    },
    /*关闭弹窗*/
    dialogFormVisible() {
      this.$emit('closeDialog', {
        type: 'error',
        openDialog: false
      });
    }
  },
  computed: {
    dialogTitle() {
      return this.isEdit ? '编辑需求' : '发布需求';
    }
  }
};
</script>
<style>
.ml10 { margin-left: 10px; }
.gray9 { color: #999; }
.upload-list {
  border: 1px solid #e5e5e5;
  border-radius: 2px;
  padding: 10px;
  min-height: 80px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.upload-list .item {
  width: 60px;
  height: 60px;
  border: 1px solid #e5e5e5;
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}
.upload-list .item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.upload-list .item .el-button {
  position: absolute;
  top: 2px;
  right: 2px;
  padding: 2px 6px;
  background: rgba(0, 0, 0, 0.6);
  border: none;
  color: #fff;
  border-radius: 2px;
}
.upload-list .img-select {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed #d9d9d9;
  background: #fafafa;
  cursor: pointer;
}
.upload-list .img-select i {
  font-size: 24px;
  color: #c0c4cc;
}
.upload-list .img-select:hover {
  border-color: #c0c4cc;
  background: #ecf5ff;
}
</style>
shop_vue/src/views/plus/release/demandproject/index.vue
@@ -18,6 +18,7 @@
        </el-form-item>
        <el-form-item>
          <el-button size="small" type="primary" icon="el-icon-search" @click="onSubmit">查询</el-button>
          <el-button size="small" type="success" icon="el-icon-plus" @click="addClick">发布需求</el-button>
        </el-form-item>
      </el-form>
    </div>
@@ -45,7 +46,8 @@
          <el-table-column fixed="right" label="操作" width="200">
            <template slot-scope="scope">
              <el-button v-if="scope.row.status == 0" @click="shClick(scope.row)" type="text" size="small">审核</el-button>
              <el-button @click="editClick(scope.row)" type="text" size="small" >详情</el-button>
              <el-button @click="editProjectClick(scope.row)" type="text" size="small">编辑</el-button>
              <el-button @click="editClick(scope.row)" type="text" size="small">详情</el-button>
              <el-button @click="evaluateClick(scope.row)" type="text" size="small" >评论列表</el-button>
              <el-button @click="deleteClick(scope.row)" type="text" size="small">删除</el-button>
            </template>
@@ -65,6 +67,9 @@
    <Edit v-if="open_edit" :open_edit="open_edit" :form="userModel" @closeDialog="closeDialogFunc($event, 'edit')"></Edit>
    <Editsh v-if="open_sh" :open_edit="open_sh" :form="userModel" @closeDialog="closeDialogFunc($event, 'editsh')"></Editsh>
    <!--发布需求/编辑需求-->
    <Add v-if="open_add" :open_add="open_add" :edit_data="editData" @closeDialog="closeDialogFunc($event, 'add')"></Add>
  </div>
</template>
@@ -72,12 +77,14 @@
  import PlusApi from '@/api/plus/release.js';
  import Edit from './Edit.vue';
  import Editsh from './dialog/Edit.vue';
  import Add from './dialog/Add.vue';
  import {deepClone} from '@/utils/base.js';
  export default {
    components: {
      /*编辑组件*/
      Edit,
      Editsh,
      Add,
    },
    data() {
      return {
@@ -102,6 +109,9 @@
        /*当前编辑的对象*/
        userModel: {},
        open_sh:false,
        open_add: false,
        /*编辑数据*/
        editData: null,
      };
    },
    created() {
@@ -164,6 +174,18 @@
        this.open_edit = true;
      },
      /*打开发布弹窗*/
      addClick() {
        this.editData = null;
        this.open_add = true;
      },
      /*打开编辑弹窗*/
      editProjectClick(item) {
        this.editData = deepClone(item);
        this.open_add = true;
      },
      /*打开弹出层审核*/
      shClick(item) {
        this.userModel = item;
shop_vue/src/views/plus/release/supplyproject/dialog/Add.vue
New file
@@ -0,0 +1,358 @@
<template>
  <!--
      作者:yj
      后台发布供应
    -->
  <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" @close="dialogFormVisible" :close-on-click-modal="false" :close-on-press-escape="false" width="800px">
    <el-form size="small" :model="form" ref="form" :rules="rules" label-width="120px">
      <el-form-item label="发布用户" prop="user_id">
        <el-input v-model="selectedUserName" placeholder="请选择用户" disabled style="width: 200px;"></el-input>
        <el-button type="primary" @click="openUserSelect">选择用户</el-button>
        <span v-if="selectedUser" class="ml10 gray9">连盟币: {{ selectedUser.points }}</span>
      </el-form-item>
      <el-form-item label="标题" prop="name">
        <el-input v-model="form.name" placeholder="请输入标题" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="分类" prop="category_id">
        <el-select v-model="form.category_id" placeholder="请选择分类" style="width: 200px;">
          <el-option v-for="item in categoryList" :key="item.category_id" :label="item.name" :value="item.category_id"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="价格(元)" prop="price">
        <el-input v-model="form.price" placeholder="请输入价格" style="width: 200px;"></el-input>
      </el-form-item>
      <el-form-item label="详细描述" prop="content">
        <el-input type="textarea" :rows="3" v-model="form.content" placeholder="请输入详细描述" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="图片">
        <div class="upload-list d-s-c">
          <div class="item" v-for="(imgs, img_num) in form.image_list" :key="img_num">
            <img :src="imgs.file_path" style="width: 60px; height: 60px; margin-right: 10px;" />
            <el-button type="text" size="mini" @click="deleteImg(img_num)">删除</el-button>
          </div>
          <div class="item img-select" @click="openUpload"><i class="el-icon-plus"></i></div>
        </div>
      </el-form-item>
      <el-form-item label="标签">
        <el-checkbox-group v-model="form.tag_list">
          <el-checkbox v-for="item in tagList" :key="item.tag_id" :label="item.tag_id">{{ item.name }}</el-checkbox>
        </el-checkbox-group>
      </el-form-item>
      <el-form-item label="交付时间">
        <el-date-picker v-model="form.finish_time" type="date" placeholder="选择日期" value-format="yyyy-MM-dd" style="width: 200px;"></el-date-picker>
      </el-form-item>
      <el-form-item label="服务地区">
        <el-input v-model="form.detail" placeholder="请输入服务地区" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="产品介绍">
        <el-input type="textarea" :rows="3" v-model="form.product_content" placeholder="请输入产品介绍" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="产品应用案例">
        <el-input type="textarea" :rows="3" v-model="form.product_case" placeholder="请输入产品应用案例" style="width: 400px;"></el-input>
      </el-form-item>
      <el-form-item label="是否展示">
        <el-radio-group v-model="form.is_show">
          <el-radio :label="1">是</el-radio>
          <el-radio :label="0">否</el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="支付连盟币" v-if="!isEdit">
        <el-radio-group v-model="payPoints">
          <el-radio :label="0">不支付</el-radio>
          <el-radio :label="1">支付 ({{ supplyPrice }} 连盟币)</el-radio>
        </el-radio-group>
      </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
      <el-button @click="dialogFormVisible">取 消</el-button>
      <el-button type="primary" @click="submitForm" :loading="loading">确 定</el-button>
    </div>
    <!-- 供应方用户选择器 -->
    <GetSupplyUser :is_open="showUserSelector" @close="handleUserSelect"></GetSupplyUser>
    <!-- 图片上传组件 -->
    <Upload v-if="isUpload" :config="uploadConfig" :isupload="isUpload" @returnImgs="returnImgsFunc">上传图片</Upload>
  </el-dialog>
</template>
<script>
import PlusApi from '@/api/plus/release.js';
import GetSupplyUser from '@/components/release/GetSupplyUser.vue';
import Upload from '@/components/file/Upload';
import { deepClone } from '@/utils/base.js';
export default {
  components: {
    GetSupplyUser,
    Upload
  },
  data() {
    return {
      /*是否显示*/
      dialogVisible: false,
      loading: false,
      formLabelWidth: '120px',
      /*是否编辑模式*/
      isEdit: false,
      /*编辑的项目ID*/
      editProjectId: null,
      /*表单数据*/
      form: {
        user_id: '',
        name: '',
        category_id: '',
        price: '',
        content: '',
        image_list: [],
        tag_list: [],
        finish_time: '',
        detail: '',
        product_content: '',
        product_case: '',
        is_show: 1
      },
      /*验证规则*/
      rules: {
        user_id: [{ required: true, message: '请选择发布用户', trigger: 'change' }],
        name: [{ required: true, message: '请输入标题', trigger: 'blur' }],
        category_id: [{ required: true, message: '请选择分类', trigger: 'change' }],
        price: [{ required: true, message: '请输入价格', trigger: 'blur' }],
        content: [{ required: true, message: '请输入详细描述', trigger: 'blur' }]
      },
      /*分类列表*/
      categoryList: [],
      /*标签列表*/
      tagList: [],
      /*选中的用户*/
      selectedUser: null,
      selectedUserName: '',
      /*是否显示用户选择器*/
      showUserSelector: false,
      /*是否支付连盟币*/
      payPoints: 0,
      /*支付连盟币金额(从设置获取)*/
      supplyPrice: 0,
      /*是否显示上传组件*/
      isUpload: false,
      /*上传配置*/
      uploadConfig: {
        total: 9,
        file_type: 'image'
      }
    };
  },
  props: ['open_add', 'edit_data'],
  created() {
    this.dialogVisible = this.open_add;
    this.getDefaultData();
    // 判断是否为编辑模式
    if (this.edit_data && this.edit_data.project_id) {
      this.isEdit = true;
      this.editProjectId = this.edit_data.project_id;
      this.initEditData();
    }
  },
  methods: {
    /*获取默认数据*/
    getDefaultData() {
      let self = this;
      PlusApi.supplyProjectDefaultData({}, true).then(data => {
        self.categoryList = data.data.data.category_list || [];
        self.tagList = data.data.data.tag_list || [];
        self.supplyPrice = data.data.data.supply_price || 0;
      }).catch(error => {});
    },
    /*初始化编辑数据*/
    initEditData() {
      const data = this.edit_data;
      this.selectedUser = {
        user_id: data.user_id,
        nickName: data.nickName || (data.user && data.user.nickName),
        points: data.points || (data.user && data.user.points)
      };
      this.selectedUserName = this.selectedUser.nickName || ('用户ID:' + data.user_id);
      this.form.user_id = data.user_id;
      this.form.name = data.name;
      this.form.category_id = data.category_id;
      this.form.price = data.price;
      this.form.content = data.content;
      this.form.image_list = data.image_list || [];
      // 提取tag_id数组
      this.form.tag_list = (data.tag_list && data.tag_list.map) ? data.tag_list.map(item => item.tag_id) : [];
      this.form.finish_time = data.finish_time || '';
      this.form.detail = data.detail || '';
      this.form.product_content = data.product_content || '';
      this.form.product_case = data.product_case || '';
      this.form.is_show = data.is_show;
    },
    /*打开用户选择器*/
    openUserSelect() {
      this.showUserSelector = true;
    },
    /*打开上传组件*/
    openUpload() {
      this.isUpload = true;
    },
    /*处理用户选择*/
    handleUserSelect(result) {
      this.showUserSelector = false;
      if (result && result.type === 'success' && result.params) {
        const user = result.params;
        this.selectedUser = user;
        this.selectedUserName = user.nickName || user.real_name || ('用户ID:' + user.user_id);
        this.form.user_id = user.user_id;
      }
    },
    /*处理上传图片返回*/
    returnImgsFunc(e) {
      if (e != null) {
        this.form.image_list = this.form.image_list.concat(e);
      }
      this.isUpload = false;
    },
    /*删除图片*/
    deleteImg(index) {
      this.form.image_list.splice(index, 1);
    },
    /*提交表单*/
    submitForm() {
      let self = this;
      self.$refs.form.validate(valid => {
        if (valid) {
          if (!self.selectedUser || !self.selectedUser.user_id) {
            self.$message.error('请选择发布用户');
            return;
          }
          self.loading = true;
          let params = {
            user_id: self.selectedUser.user_id,
            formData: JSON.stringify(self.form)
          };
          if (self.isEdit) {
            // 编辑模式
            params.project_id = self.editProjectId;
            PlusApi.supplyProjectEdit(params, true).then(data => {
              self.loading = false;
              self.$message.success('编辑成功');
              self.$emit('closeDialog', {
                type: 'success',
                openDialog: false
              });
            }).catch(error => {
              self.loading = false;
            });
          } else {
            // 新增模式
            params.pay_points = self.payPoints;
            PlusApi.supplyProjectAdd(params, true).then(data => {
              self.loading = false;
              self.$message.success('发布成功');
              self.$emit('closeDialog', {
                type: 'success',
                openDialog: false
              });
            }).catch(error => {
              self.loading = false;
            });
          }
        }
      });
    },
    /*关闭弹窗*/
    dialogFormVisible() {
      this.$emit('closeDialog', {
        type: 'error',
        openDialog: false
      });
    }
  },
  computed: {
    dialogTitle() {
      return this.isEdit ? '编辑供应' : '发布供应';
    }
  }
};
</script>
<style>
.ml10 { margin-left: 10px; }
.gray9 { color: #999; }
.upload-list {
  border: 1px solid #e5e5e5;
  border-radius: 2px;
  padding: 10px;
  min-height: 80px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.upload-list .item {
  width: 60px;
  height: 60px;
  border: 1px solid #e5e5e5;
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}
.upload-list .item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.upload-list .item .el-button {
  position: absolute;
  top: 2px;
  right: 2px;
  padding: 2px 6px;
  background: rgba(0, 0, 0, 0.6);
  border: none;
  color: #fff;
  border-radius: 2px;
}
.upload-list .img-select {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed #d9d9d9;
  background: #fafafa;
  cursor: pointer;
}
.upload-list .img-select i {
  font-size: 24px;
  color: #c0c4cc;
}
.upload-list .img-select:hover {
  border-color: #c0c4cc;
  background: #ecf5ff;
}
</style>
shop_vue/src/views/plus/release/supplyproject/index.vue
@@ -18,6 +18,7 @@
        </el-form-item>
        <el-form-item>
          <el-button size="small" type="primary" icon="el-icon-search" @click="onSubmit">查询</el-button>
          <el-button size="small" type="success" icon="el-icon-plus" @click="addClick">发布供应</el-button>
        </el-form-item>
      </el-form>
    </div>
@@ -39,7 +40,8 @@
          <el-table-column fixed="right" label="操作" width="200">
            <template slot-scope="scope">
              <el-button v-if="scope.row.status == 0" @click="shClick(scope.row)" type="text" size="small">审核</el-button>
              <el-button @click="editClick(scope.row)" type="text" size="small" >详情</el-button>
              <el-button @click="editProjectClick(scope.row)" type="text" size="small">编辑</el-button>
              <el-button @click="editClick(scope.row)" type="text" size="small">详情</el-button>
              <el-button @click="evaluateClick(scope.row)" type="text" size="small" >评论列表</el-button>
              <el-button @click="deleteClick(scope.row)" type="text" size="small">删除</el-button>
            </template>
@@ -59,6 +61,9 @@
    <Edit v-if="open_edit" :open_edit="open_edit" :form="userModel" @closeDialog="closeDialogFunc($event, 'edit')"></Edit>
    <Editsh v-if="open_sh" :open_edit="open_sh" :form="userModel" @closeDialog="closeDialogFunc($event, 'editsh')"></Editsh>
    <!--发布供应/编辑供应-->
    <Add v-if="open_add" :open_add="open_add" :edit_data="editData" @closeDialog="closeDialogFunc($event, 'add')"></Add>
  </div>
</template>
@@ -66,6 +71,7 @@
  import PlusApi from '@/api/plus/release.js';
  import Edit from './Edit.vue';
  import Editsh from './dialog/Edit.vue';
  import Add from './dialog/Add.vue';
  import {deepClone} from '@/utils/base.js';
  export default {
@@ -73,6 +79,7 @@
      /*编辑组件*/
      Edit,
      Editsh,
      Add,
    },
    data() {
      return {
@@ -97,6 +104,9 @@
        /*当前编辑的对象*/
        userModel: {},
        open_sh:false,
        open_add: false,
        /*编辑数据*/
        editData: null,
      };
    },
    created() {
@@ -159,6 +169,18 @@
        this.open_edit = true;
      },
      /*打开发布弹窗*/
      addClick() {
        this.editData = null;
        this.open_add = true;
      },
      /*打开编辑弹窗*/
      editProjectClick(item) {
        this.editData = deepClone(item);
        this.open_add = true;
      },
      /*打开弹出层审核*/
      shClick(item) {
        this.userModel = item;