| New file |
| | |
| | | <?php |
| | | namespace app\api\controller\plus\business; |
| | | |
| | | use app\api\controller\Controller; |
| | | use app\api\model\plus\business\Business as BusinessModel; |
| | | |
| | | class Business extends Controller |
| | | { |
| | | /** |
| | | * 获取名片列表 |
| | | */ |
| | | public function getList() |
| | | { |
| | | $model = new BusinessModel(); |
| | | return $this->renderSuccess('',$model->getLists()); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\controller\plus\business; |
| | | |
| | | use app\api\controller\Controller; |
| | | use app\api\model\plus\business\Grade as GradeModel; |
| | | |
| | | class Grade extends Controller |
| | | { |
| | | /** |
| | | * 获取等级列表 |
| | | */ |
| | | public function getList() |
| | | { |
| | | $model = new GradeModel(); |
| | | return $this->renderSuccess('',$model->getLists()); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\controller\plus\business; |
| | | |
| | | use app\api\controller\Controller; |
| | | use app\api\model\plus\business\Industry as IndustryModel; |
| | | |
| | | class Industry extends Controller |
| | | { |
| | | /** |
| | | * 获取所有行业(树状结构) |
| | | */ |
| | | public function getIndustryTree() |
| | | { |
| | | $tree = IndustryModel::getCacheTree(); |
| | | return $this->renderSuccess(compact('tree')); |
| | | } |
| | | |
| | | /** |
| | | * 获取所有行业列表 |
| | | */ |
| | | public function getIndustryList() |
| | | { |
| | | $list = IndustryModel::getCacheAll(); |
| | | return $this->renderSuccess(compact('list')); |
| | | } |
| | | |
| | | /** |
| | | * 获取行业详情 |
| | | */ |
| | | public function detail($industry_id) |
| | | { |
| | | $industry = IndustryModel::detail($industry_id); |
| | | if (!$industry) { |
| | | return $this->renderError('行业不存在'); |
| | | } |
| | | return $this->renderSuccess(compact('industry')); |
| | | } |
| | | |
| | | /** |
| | | * 获取一级行业列表 |
| | | */ |
| | | public function getFirstIndustry() |
| | | { |
| | | $list = IndustryModel::getFirstIndustry(); |
| | | return $this->renderSuccess(compact('list')); |
| | | } |
| | | |
| | | /** |
| | | * 根据上级ID获取子行业 |
| | | */ |
| | | public function getSubIndustry($parent_id = 0) |
| | | { |
| | | $model = new IndustryModel; |
| | | $list = $model->where('parent_id', '=', $parent_id)->where('status', '=', 1) |
| | | ->order(['sort' => 'asc', 'create_time' => 'asc'])->select(); |
| | | return $this->renderSuccess(compact('list')); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\controller\plus\business; |
| | | |
| | | use app\api\controller\Controller; |
| | | use app\api\model\plus\business\Saving as SavingModel; |
| | | |
| | | class Saving extends Controller |
| | | { |
| | | /** |
| | | * 获取名片记录列表 |
| | | */ |
| | | public function getList() |
| | | { |
| | | $model = new SavingModel(); |
| | | return $this->renderSuccess('',$model->getLists()); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\controller\plus\business; |
| | | |
| | | use app\api\controller\Controller; |
| | | use app\api\model\plus\business\Template as TemplateModel; |
| | | |
| | | class Template extends Controller |
| | | { |
| | | /** |
| | | * 获取模板列表 |
| | | */ |
| | | public function getList() |
| | | { |
| | | $model = new TemplateModel(); |
| | | return $this->renderSuccess('',$model->getLists()); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\model\plus\business; |
| | | use app\common\model\plus\business\Business as CommonBusiness; |
| | | class Business extends CommonBusiness |
| | | { |
| | | |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\model\plus\business; |
| | | use app\common\model\plus\business\Grade as CommonGrade; |
| | | class Grade extends CommonGrade |
| | | { |
| | | |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\model\plus\business; |
| | | |
| | | use app\common\model\plus\business\Industry as CommonIndustry; |
| | | |
| | | class Industry extends CommonIndustry |
| | | { |
| | | |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\model\plus\business; |
| | | use app\common\model\plus\business\Saving as CommonSaving; |
| | | /** |
| | | * 名片记录模型 |
| | | */ |
| | | class Saving extends CommonSaving |
| | | { |
| | | |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\api\model\plus\business; |
| | | use app\common\model\plus\business\Template as CommonTemplate; |
| | | class Template extends CommonTemplate |
| | | { |
| | | |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\common\model\plus\business; |
| | | use app\common\model\BaseModel; |
| | | |
| | | /** |
| | | * 名片管理模型 |
| | | */ |
| | | class Business extends BaseModel |
| | | { |
| | | protected $name='business_card'; |
| | | |
| | | public function getSexAttr($value){ |
| | | $data=[10=>'未知',20=>'男',30=>'女']; |
| | | return $data[$value]; |
| | | } |
| | | /** |
| | | * 关联用户 |
| | | * @return \think\model\relation\HasOne |
| | | */ |
| | | public function user(){ |
| | | $model=self::getCalledModule()?:'common'; |
| | | return $this->hasOne("app\\$model\\model\\user\\User",'user_id','user_id'); |
| | | } |
| | | |
| | | /** |
| | | * 头像 |
| | | * @return \think\model\relation\HasOne |
| | | */ |
| | | public function image(){ |
| | | $model=self::getCalledModule()?:'common'; |
| | | return $this->hasOne("app\\$model\\model\\file\\UploadFile",'file_id','file_id')->bind(['file_path']); |
| | | } |
| | | /** |
| | | * logo |
| | | * @return \think\model\relation\HasOne |
| | | */ |
| | | public function logoImage(){ |
| | | $model=self::getCalledModule()?:'common'; |
| | | return $this->hasOne("app\\$model\\model\\file\\UploadFile",'file_id','logo')->bind(['file_path']); |
| | | } |
| | | /** |
| | | * 添加 |
| | | * @param $data |
| | | * @return false|int |
| | | */ |
| | | public function add($data){ |
| | | $data['app_id']=self::$app_id; |
| | | return $this->save($data); |
| | | } |
| | | public function getUnitAttr($name) |
| | | { |
| | | return json_decode($name); |
| | | } |
| | | public function getDutiesAttr($name) |
| | | { |
| | | return json_decode($name); |
| | | } |
| | | public function getAddressAttr($name) |
| | | { |
| | | return json_decode($name); |
| | | } |
| | | public function getPositionAttr($name) |
| | | { |
| | | return $name?json_decode($name):[]; |
| | | } |
| | | |
| | | /** |
| | | * 获取名片列表 |
| | | * @param $param |
| | | * @return \think\Paginator |
| | | * @throws \think\db\exception\DbException |
| | | */ |
| | | public function getList($param=[]){ |
| | | $paramr=array_merge(['listRow'=>15],$param); |
| | | $where=[]; |
| | | !empty($paramr['name'])&&$where['name']=['like','%'.$paramr['name'].'%']; |
| | | !empty($paramr['search'])&&$where['name|duties|unit']=['like','%'.$paramr['search'].'%']; |
| | | !empty($paramr['user_id'])&&$where['user_id']=$paramr['user_id']; |
| | | if(!empty($paramr['sort'])){ |
| | | if($paramr['sort']=='name'){ |
| | | $order=['name'=>"asc"]; |
| | | }else if($paramr['sort']=='time'){ |
| | | $order=['create_time'=>"asc"]; |
| | | }else{ |
| | | $order=['unit'=>"asc"]; |
| | | } |
| | | }else{ |
| | | $order=['is_default'=>'desc','create_time'=>'desc']; |
| | | } |
| | | return $this->with(['user','image','logoImage'])->order($order)->where($where)->paginate($paramr); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\common\model\plus\business; |
| | | |
| | | use app\common\model\BaseModel; |
| | | |
| | | /** |
| | | * 名片等级模型 |
| | | */ |
| | | class Grade extends BaseModel |
| | | { |
| | | protected $pk = 'grade_id'; |
| | | protected $name = 'business_card_grade'; |
| | | |
| | | /** |
| | | * 名片等级模型初始化 |
| | | */ |
| | | public static function init() |
| | | { |
| | | parent::init(); |
| | | } |
| | | |
| | | /** |
| | | * 获取详情 |
| | | */ |
| | | public static function detail($grade_id) |
| | | { |
| | | return (new static())->find($grade_id); |
| | | } |
| | | |
| | | /** |
| | | * 获取列表记录 |
| | | */ |
| | | public function getLists() |
| | | { |
| | | return $this->where('app_id', '=', self::$app_id) |
| | | ->order(['weight' => 'asc', 'create_time' => 'asc']) |
| | | ->select(); |
| | | } |
| | | |
| | | /** |
| | | * 查询列表 |
| | | */ |
| | | public function selectList() |
| | | { |
| | | return $this->where('app_id', '=', self::$app_id) |
| | | ->order(['weight' => 'asc', 'create_time' => 'asc']) |
| | | ->select(); |
| | | } |
| | | |
| | | /** |
| | | * 获取默认等级id |
| | | */ |
| | | public static function getDefaultGradeId() |
| | | { |
| | | $model = new static(); |
| | | $grade = $model->where('app_id', '=', self::$app_id)->where('weight', '=', 1)->find(); |
| | | if (!$grade) { |
| | | $model->save([ |
| | | 'name' => '默认等级', |
| | | 'price' => 0.00, |
| | | 'weight' => 1, |
| | | 'app_id' => self::$app_id |
| | | ]); |
| | | $grade_id = $model['grade_id']; |
| | | } else { |
| | | $grade_id = $grade['grade_id']; |
| | | } |
| | | return $grade_id; |
| | | } |
| | | |
| | | /** |
| | | * 新增等级 |
| | | */ |
| | | public function add($data) |
| | | { |
| | | $data['app_id'] = self::$app_id; |
| | | return $this->save($data); |
| | | } |
| | | |
| | | /** |
| | | * 编辑等级 |
| | | */ |
| | | public function edit($data) |
| | | { |
| | | return $this->save($data); |
| | | } |
| | | |
| | | /** |
| | | * 删除等级 |
| | | */ |
| | | public function setDelete() |
| | | { |
| | | // 检查是否有用户使用此等级 |
| | | if (class_exists('app\common\model\plus\business\Business')) { |
| | | $businessModel = new Business(); |
| | | $count = $businessModel->where('grade_id', '=', $this['grade_id'])->count(); |
| | | if ($count > 0) { |
| | | return false; |
| | | } |
| | | } |
| | | return $this->delete(); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\common\model\plus\business; |
| | | |
| | | use think\facade\Cache; |
| | | use app\common\model\BaseModel; |
| | | |
| | | /** |
| | | * 行业模型 |
| | | */ |
| | | class Industry extends BaseModel |
| | | { |
| | | protected $pk = 'industry_id'; |
| | | protected $name = 'industry'; |
| | | |
| | | /** |
| | | * 行业详情 |
| | | */ |
| | | public static function detail($industry_id) |
| | | { |
| | | return (new static())->find($industry_id); |
| | | } |
| | | |
| | | /** |
| | | * 所有行业 |
| | | */ |
| | | public static function getALL() |
| | | { |
| | | $model = new static; |
| | | if (!Cache::get('industry_' . $model::$app_id)) { |
| | | $data = $model->order(['sort' => 'asc', 'create_time' => 'asc'])->select(); |
| | | $all = !empty($data) ? $data->toArray() : []; |
| | | $tree = []; |
| | | foreach ($all as $first) { |
| | | if ($first['parent_id'] != 0) continue; |
| | | $twoTree = []; |
| | | foreach ($all as $two) { |
| | | if ($two['parent_id'] != $first['industry_id']) continue; |
| | | $threeTree = []; |
| | | foreach ($all as $three) |
| | | $three['parent_id'] == $two['industry_id'] |
| | | && $threeTree[$three['industry_id']] = $three; |
| | | !empty($threeTree) && $two['child'] = $threeTree; |
| | | array_push($twoTree, $two); |
| | | } |
| | | if (!empty($twoTree)) { |
| | | $temp_two_tree = array_column($twoTree, 'sort'); |
| | | array_multisort($temp_two_tree, SORT_ASC, $twoTree); |
| | | $first['child'] = $twoTree; |
| | | } |
| | | array_push($tree, $first); |
| | | } |
| | | Cache::tag('cache')->set('industry_' . $model::$app_id, compact('all', 'tree')); |
| | | } |
| | | return Cache::get('industry_' . $model::$app_id); |
| | | } |
| | | |
| | | /** |
| | | * 获取所有行业 |
| | | */ |
| | | public static function getCacheAll() |
| | | { |
| | | return self::getALL()['all']; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有行业(树状结构) |
| | | */ |
| | | public static function getCacheTree() |
| | | { |
| | | return self::getALL()['tree']; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有行业(树状结构) |
| | | * @return string |
| | | */ |
| | | public static function getCacheTreeJson() |
| | | { |
| | | return json_encode(static::getCacheTree()); |
| | | } |
| | | |
| | | /** |
| | | * 获取指定行业下的所有子行业id |
| | | */ |
| | | public static function getSubIndustryId($parent_id, $all = []) |
| | | { |
| | | $arrIds = [$parent_id]; |
| | | empty($all) && $all = self::getCacheAll(); |
| | | foreach ($all as $key => $item) { |
| | | if ($item['parent_id'] == $parent_id) { |
| | | unset($all[$key]); |
| | | $subIds = self::getSubIndustryId($item['industry_id'], $all); |
| | | !empty($subIds) && $arrIds = array_merge($arrIds, $subIds); |
| | | } |
| | | } |
| | | return $arrIds; |
| | | } |
| | | |
| | | /** |
| | | * 指定的行业下是否存在子行业 |
| | | */ |
| | | protected static function hasSubIndustry($parentId) |
| | | { |
| | | $all = self::getCacheAll(); |
| | | foreach ($all as $item) { |
| | | if ($item['parent_id'] == $parentId) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有一级行业 |
| | | */ |
| | | public static function getFirstIndustry() |
| | | { |
| | | return (new static())->where('parent_id', '=', 0) |
| | | ->order(['sort' => 'asc', 'create_time' => 'asc']) |
| | | ->select(); |
| | | } |
| | | |
| | | public function getListByIds($ids) |
| | | { |
| | | return $this->field(['industry_id', 'name', 'parent_id'])->where('industry_id', 'in', $ids)->select(); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\common\model\plus\business; |
| | | use app\common\model\BaseModel; |
| | | |
| | | /** |
| | | * 名片保存(浏览)记录 |
| | | */ |
| | | class Saving extends BaseModel |
| | | { |
| | | protected $name='business_card_saving'; |
| | | public function user() |
| | | { |
| | | $model=self::getCalledModule()?:'common'; |
| | | return $this->hasOne("\\app\\$model\\model\\user\\User",'user_id','user_id'); |
| | | } |
| | | public function affiliation() |
| | | { |
| | | $model=self::getCalledModule()?:'common'; |
| | | return $this->hasOne("\\app\\$model\\model\\user\\User",'user_id','affiliation_id'); |
| | | } |
| | | public function business() |
| | | { |
| | | $model=self::getCalledModule()?:'common'; |
| | | return $this->hasOne("\\app\\$model\\model\\plus\\business\\Business",'business_card_id','business_card_id'); |
| | | } |
| | | |
| | | public function lists($param=[]){ |
| | | $paramL=array_merge(['listRows'=>15],$param); |
| | | $where=[]; |
| | | !empty($paramL['user_id'])&&$where['user_id']=$paramL['user_id']; |
| | | !empty($paramL['affiliation_id'])&&$where['affiliation_id']=$paramL['affiliation_id']; |
| | | !empty($paramL['type'])&&$where['type']=$paramL['type']; |
| | | return $this->with(['user'=>['businessCard'=>['image','logoImage']],'affiliation','business'=>['image']])->order('create_time','desc')->where($where)->paginate($paramL); |
| | | } |
| | | |
| | | /** |
| | | * 获取记录数量 |
| | | * @param $param |
| | | * @return int|string |
| | | * @throws \think\Exception |
| | | */ |
| | | public function getQuantity($paramL=[]) |
| | | { |
| | | $where=[]; |
| | | !empty($paramL['user_id'])&&$where['user_id']=$paramL['user_id']; |
| | | !empty($paramL['affiliation_id'])&&$where['affiliation_id']=$paramL['affiliation_id']; |
| | | !empty($paramL['today'])&&$this->whereTime('create_time', 'today'); |
| | | !empty($paramL['type'])&&$where['type']=$paramL['type']; |
| | | return $this->where($where)->count(); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\common\model\plus\business; |
| | | |
| | | use app\common\model\BaseModel; |
| | | |
| | | /** |
| | | * 名片模板 |
| | | */ |
| | | class Template extends BaseModel |
| | | { |
| | | protected $name='business_card_template'; |
| | | protected $pk='template_id'; |
| | | public function getList($param=[]){ |
| | | $paramL=array_merge(['listRows'=>15],$param); |
| | | $where=[]; |
| | | return $this->order('create_time','desc')->where($where)->paginate($paramL); |
| | | } |
| | | public function add($data){ |
| | | $data['app_id']=self::$app_id; |
| | | return $this->save($data); |
| | | } |
| | | |
| | | /** |
| | | * 获取模板详情 |
| | | * @param $template_id |
| | | * @return array|false|\PDOStatement|string|Template |
| | | * @throws \think\db\exception\DataNotFoundException |
| | | * @throws \think\db\exception\ModelNotFoundException |
| | | * @throws \think\exception\DbException |
| | | */ |
| | | public function detail($template_id) |
| | | { |
| | | return $this->where(['template_id'=>$template_id])->find(); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\common\service\business; |
| | | |
| | | use app\common\model\plus\business\Template; |
| | | use app\common\service\qrcode\Base; |
| | | use Grafika\Color; |
| | | use Grafika\Grafika; |
| | | |
| | | /** |
| | | * 生成名片 |
| | | * Class Qrcode |
| | | * @package app\common\service |
| | | */ |
| | | class Poster extends Base |
| | | { |
| | | /* @var array $dealer 用户信息 */ |
| | | private $dealer; |
| | | |
| | | /* @var array $config 名片设置 */ |
| | | private $config; |
| | | public $template; |
| | | |
| | | /** |
| | | * 构造方法 |
| | | * Poster constructor. |
| | | * @param $dealer |
| | | * @throws \Exception |
| | | */ |
| | | public function __construct($dealer) |
| | | { |
| | | parent::__construct(); |
| | | // 用户信息 |
| | | $this->dealer = $dealer; |
| | | $this->template = (new Template())->detail($dealer['template_id']); |
| | | // 名片设置 |
| | | $this->config = json_decode($this->template['style'], true); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户名片 |
| | | * @return string |
| | | * @throws \app\common\exception\BaseException |
| | | * @throws \think\exception\DbException |
| | | * @throws \Exception |
| | | */ |
| | | public function getImage($isType = true) |
| | | { |
| | | if (file_exists($this->getPosterPath('business')) && $isType) { |
| | | return $this->getPosterUrl('business'); |
| | | } |
| | | // 小程序id |
| | | $appId = $this->dealer['app_id']; |
| | | // 1. 下载背景图 |
| | | $backdrop = $this->saveTempImage($appId, $this->config['backdrop']['src'], 'backdrop'); |
| | | if ($this->dealer['file_path']) { |
| | | // 2. 下载用户头像 |
| | | $avatarUrl = $this->saveTempImage($appId, $this->dealer['file_path'], 'avatar'); |
| | | } else { |
| | | // 2. 下载用户头像 |
| | | $avatarUrl = $this->saveTempImage($appId, $this->dealer['user']['avatarUrl'], 'avatar'); |
| | | } |
| | | $logo = ''; |
| | | if ($this->dealer['logoImage']['file_path']) { |
| | | // 2. 下载logo |
| | | $logo = $this->saveTempImage($appId, $this->dealer['logoImage']['file_path'], 'logo'); |
| | | } |
| | | // 4. 拼接名片 |
| | | return $this->savePoster($backdrop, $avatarUrl, 'business', $logo); |
| | | } |
| | | |
| | | /** |
| | | * 名片图文件路径 |
| | | * @return string |
| | | */ |
| | | private function getPosterPath($name) |
| | | { |
| | | // 保存路径 |
| | | $tempPath = $_SERVER['DOCUMENT_ROOT']. '/temp/'.$this->template['app_id'] . '/'.$name.'/'; |
| | | !is_dir($tempPath) && mkdir($tempPath, 0755, true); |
| | | return $tempPath . $this->getPosterName(); |
| | | } |
| | | |
| | | /** |
| | | * 名片文件名称 |
| | | * @return string |
| | | */ |
| | | private function getPosterName() |
| | | { |
| | | return md5('poster_' . $this->dealer['business_card_id']) . '.png'; |
| | | } |
| | | |
| | | /** |
| | | * 名片url |
| | | * @return string |
| | | */ |
| | | private function getPosterUrl($name) |
| | | { |
| | | return base_url() . '/temp/' . $this->template['app_id'] . '/' .$name . '/' . $this->getPosterName() . '?t=' . time(); |
| | | } |
| | | |
| | | /** |
| | | * 拼接名片 |
| | | * @param $backdrop |
| | | * @param $avatarUrl |
| | | * @param $imageUrl |
| | | * @param $logo |
| | | * @return string |
| | | * @throws \Exception |
| | | */ |
| | | private function savePoster($backdrop, $avatarUrl, $imageUrl, $logo) |
| | | { |
| | | |
| | | // 创建画布 |
| | | list($width, $height) = getimagesize($backdrop); |
| | | $newImage = imagecreatetruecolor($width, $height); |
| | | $backdropIm = $this->imagEcr($backdrop); |
| | | imageantialias($newImage, true); |
| | | // 将第一张图片覆盖在新图像上 |
| | | imagecopy($newImage, $backdropIm, 0, 0, 0, 0, $width, $height); |
| | | if (!empty($this->config['icon'])) { |
| | | foreach ($this->config['icon'] as $key => $value) { |
| | | $this->addImagecopy($newImage, $value); |
| | | } |
| | | } |
| | | |
| | | // 写入地址 |
| | | //$this->addText($newImage, 'address', '地址:', 0, false, $width); |
| | | if ($this->config['avatar']['display'] == 1) { |
| | | // 生成圆形用户头像 |
| | | $this->config['avatar']['style'] === 'circle' && $this->circular($avatarUrl, $avatarUrl); |
| | | // 重设用户头像宽高 |
| | | $avatarWidth = $this->config['avatar']['width']; |
| | | //重设图片大小 |
| | | $this->resizeExact($avatarUrl,$avatarWidth,$avatarWidth); |
| | | // 用户头像添加到背景图 |
| | | $avatarX = $this->config['avatar']['left']; |
| | | $avatarY = $this->config['avatar']['top']; |
| | | // 打开用户头像 |
| | | $avatarIm = $this->imagEcr($avatarUrl); |
| | | imagecopy($newImage, $avatarIm, $avatarX, $avatarY, 0, 0, $avatarWidth, $avatarWidth); |
| | | } |
| | | if (is_file($logo) && $this->config['logo']['display'] == 1) { |
| | | // 生成圆形logo |
| | | $this->config['logo']['style'] === 'circle' && $this->circular($logo, $logo); |
| | | //调整后大小 |
| | | $logoWidth = round($this->config['logo']['width']); |
| | | $logoHeight = round($this->config['logo']['height']); |
| | | //重设图片大小 |
| | | $this->resizeExact($logo,$logoWidth,$logoHeight); |
| | | // 用户logo添加到背景图 |
| | | $logoX = $this->config['logo']['left']; |
| | | $logoY = $this->config['logo']['top']; |
| | | // 打开用户logo |
| | | $logoImage = $this->imagEcr($logo); |
| | | imagecopy($newImage, $logoImage, $logoX, $logoY, 0, 0, $logoWidth, $logoHeight); |
| | | } |
| | | |
| | | // 写入用户昵称 |
| | | $this->addText($newImage, 'name'); |
| | | foreach ($this->dealer['unit'] as $key => $value) { |
| | | // 写入公司 |
| | | $this->addText($newImage, 'unit', '', $key, true); |
| | | } |
| | | foreach ($this->dealer['address'] as $key => $value) { |
| | | // 写入地址 |
| | | $this->addText($newImage, 'address', '', $key, true); |
| | | } |
| | | foreach ($this->dealer['duties'] as $key => $value) { |
| | | // 写入职务 |
| | | $this->addText($newImage, 'duties', '', $key, true); |
| | | } |
| | | // 写入职位 |
| | | //$this->addText($newImage, 'duties'); |
| | | if (!empty($this->dealer['position']) && !empty($this->config['position'])) { |
| | | foreach ($this->dealer['position'] as $key => $value) { |
| | | // 写入公司 |
| | | $this->addText($newImage, 'position', '', $key, true); |
| | | } |
| | | } |
| | | // 写入手机号 |
| | | $this->addText($newImage, 'mobile', '手机:'); |
| | | |
| | | // 写入微信 |
| | | if ($this->dealer['mailbox']) { |
| | | // 写入邮箱 |
| | | $this->addText($newImage, 'mailbox', '邮箱:'); |
| | | } |
| | | if ($this->dealer['phone']) { |
| | | $this->addText($newImage, 'phone', '电话:'); |
| | | } |
| | | // 保存图片 |
| | | imagejpeg($newImage, $this->getPosterPath($imageUrl),100); // 根据需要选择合适的函数(如imagepng、imagegif等) |
| | | // 清理内存 |
| | | imagedestroy($newImage); |
| | | return $this->getPosterUrl($imageUrl); |
| | | } |
| | | |
| | | /** |
| | | * 打开图片 |
| | | * @param $image |
| | | * @return false|resource |
| | | */ |
| | | public function imagEcr($image) |
| | | { |
| | | // 创建画布 |
| | | return imagecreatefromstring(file_get_contents($image)); |
| | | } |
| | | |
| | | /** |
| | | * 重设图片大小 |
| | | * @param $image |
| | | * @param $newWidth |
| | | * @param $newHeight |
| | | * @return void |
| | | */ |
| | | public function resizeExact($imageUrl, $newWidth, $newHeight ) |
| | | { |
| | | $image=imagecreatefromstring(file_get_contents($imageUrl)); |
| | | // 调整图片大小 |
| | | $targetImage = imagecreatetruecolor($newWidth, $newHeight); |
| | | imageantialias($targetImage, true); |
| | | //获取图片大小 |
| | | $width = imagesx($image); |
| | | $height = imagesy($image); |
| | | // 这一句一定要有 |
| | | imagesavealpha($targetImage, true); |
| | | $bg = imagecolorallocatealpha($targetImage, 255, 255, 255, 127); |
| | | imagefill($targetImage, 0, 0, $bg); |
| | | imagecopyresampled($targetImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); |
| | | imagepng($targetImage, $imageUrl,0); // 根据需要选择合适的函数(如imagepng、imagegif等) |
| | | // 清理内存 |
| | | imagedestroy($targetImage); |
| | | } |
| | | |
| | | /** |
| | | * 添加图标 |
| | | * @param $newImage |
| | | * @param $data |
| | | * @return void |
| | | */ |
| | | public function addImagecopy($newImage,$data){ |
| | | $src=$this->saveTempImage($this->template['app_id'], $data['src'], 'icon'); |
| | | $this->resizeExact($src,$data['width'], $data['height']); |
| | | $backdropIm = $this->imagEcr($src); |
| | | // 将第一张图片覆盖在新图像上 |
| | | imagecopy($newImage, $backdropIm, $data['left'], $data['top'], 0, 0, $data['width'], $data['height']); |
| | | } |
| | | |
| | | /** |
| | | * 写入文字 |
| | | * @param $editor |
| | | * @param $backdropImage |
| | | * @param $name |
| | | * @param $text |
| | | * @param $key |
| | | * @param $type |
| | | * @param $width |
| | | * @return array|bool |
| | | */ |
| | | public function addText($editor, $name, $text = '', $key = 0, $type = false, $width = 0) |
| | | { |
| | | $fontPath = $_SERVER['DOCUMENT_ROOT']. '/fonts/MSYH.TTC'; |
| | | if ($type) { |
| | | list($fontSize, $fontX,$fontY) = self::SizeLeftTop($this->config[$name][$key]['fontSize'], $this->config[$name][$key]['left'],$this->config[$name][$key]['top']); |
| | | $colorResource=self::colorResource($this->config[$name][$key]['color'],$editor); |
| | | return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text . $this->dealer[$name][$key]); |
| | | } else if ($name == 'mobile') { |
| | | list($fontSize, $fontX,$fontY) = self::SizeLeftTop($this->config[$name]['fontSize'], $this->config[$name]['left'],$this->config[$name]['top']); |
| | | $text = $text . $this->dealer['mobile']; |
| | | if (!empty($this->dealer['mobile_phone'])) { |
| | | $text = $text . ' / ' . $this->dealer['mobile_phone']; |
| | | } |
| | | $colorResource=self::colorResource($this->config[$name]['color'],$editor); |
| | | return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text); |
| | | } /*else if ($name == 'duties') { |
| | | list($fontSize, $fontX,$fontY) = self::SizeLeftTop($this->config[$name][$key]['fontSize'], $this->config[$name][$key]['left'],$this->config[$name][$key]['top']); |
| | | $duties = $this->dealer['duties'][$key]; |
| | | if (!empty($this->dealer['duties'][1])) { |
| | | $duties = $duties . ' / ' . $this->dealer['duties'][1]; |
| | | } |
| | | if (!empty($this->dealer['duties'][2])) { |
| | | $duties = $duties . ' / ' . $this->dealer['duties'][2]; |
| | | } |
| | | $colorResource=self::colorResource($this->config['duties'][$key]['color'],$editor); |
| | | return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text . $duties); |
| | | }*/ else if ($name == 'address') { |
| | | list($fontSize, $fontX,$fontY) = self::SizeLeftTop($this->config[$name][$key]['fontSize'], $this->config[$name][$key]['left'],$this->config[$name][$key]['top']); |
| | | $title = $this->dealer['address'][$key]; |
| | | $strlen = mb_strlen($title, 'utf-8'); |
| | | $left = $width - $this->config['address'][$key]['left'] - $fontSize; |
| | | $titleNum = bcdiv($left, $this->config['address'][$key]['fontSize']); |
| | | $the_box = $this->config['address'][$key]['fontSize'] * 3; |
| | | while ($width < ($titleNum * $this->config['address'][$key]['fontSize'] + $the_box + $fontX)) { |
| | | $titleNum--; |
| | | }; |
| | | if ($strlen > $titleNum && $titleNum) { |
| | | $strArr = self::mbStrSplit($title, $titleNum); |
| | | } |
| | | $colorResource=self::colorResource($this->config['address'][$key]['color'],$editor); |
| | | if ($strlen > $titleNum && $titleNum) { |
| | | $y = $fontY + 10; |
| | | foreach ($strArr as $k => $v) { |
| | | if ($k == 0) { |
| | | imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text . $v); |
| | | } else { |
| | | $y = $y + ($fontSize * $k); |
| | | imagettftext($editor, $fontSize, 0, $fontX + $the_box, $y, $colorResource, $fontPath, $v); |
| | | } |
| | | } |
| | | return true; |
| | | } else { |
| | | return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text . $title); |
| | | } |
| | | |
| | | } else { |
| | | list($fontSize, $fontX,$fontY) = self::SizeLeftTop($this->config[$name]['fontSize'], $this->config[$name]['left'],$this->config[$name]['top']); |
| | | $colorResource=self::colorResource($this->config[$name]['color'],$editor); |
| | | return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text . $this->dealer[$name]); // path/to/font.ttf为自定义字体路径 |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 分割字符串为数组模式 |
| | | * @param $string |
| | | * @param $len |
| | | * @return mixed |
| | | */ |
| | | private function mbStrSplit($string, $len = 1) |
| | | { |
| | | $start = 0; |
| | | $strlen = mb_strlen($string); |
| | | while ($strlen) { |
| | | $array[] = mb_substr($string, $start, $len, "utf8"); |
| | | $string = mb_substr($string, $len, $strlen, "utf8"); |
| | | $strlen = mb_strlen($string); |
| | | } |
| | | return $array; |
| | | } |
| | | |
| | | /** |
| | | * 生成圆形图片 |
| | | * @param static $imgpath 图片地址 |
| | | * @param string $saveName 保存文件名,默认空。 |
| | | */ |
| | | private function circular($imgpath, $saveName = '') |
| | | { |
| | | $srcImg = imagecreatefromstring(file_get_contents($imgpath)); |
| | | $w = imagesx($srcImg); |
| | | $h = imagesy($srcImg); |
| | | $w = $h = min($w, $h); |
| | | $newImg = imagecreatetruecolor($w, $h); |
| | | // 这一句一定要有 |
| | | imagesavealpha($newImg, true); |
| | | // 拾取一个完全透明的颜色,最后一个参数127为全透明 |
| | | $bg = imagecolorallocatealpha($newImg, 255, 255, 255, 127); |
| | | imagefill($newImg, 0, 0, $bg); |
| | | $r = $w / 2; //圆半径 |
| | | for ($x = 0; $x < $w; $x++) { |
| | | for ($y = 0; $y < $h; $y++) { |
| | | $rgbColor = imagecolorat($srcImg, $x, $y); |
| | | if (((($x - $r) * ($x - $r) + ($y - $r) * ($y - $r)) < ($r * $r))) { |
| | | imagesetpixel($newImg, $x, $y, $rgbColor); |
| | | } |
| | | } |
| | | } |
| | | // 输出图片到文件 |
| | | imagepng($newImg, $saveName,0); |
| | | // 释放空间 |
| | | imagedestroy($srcImg); |
| | | imagedestroy($newImg); |
| | | } |
| | | |
| | | /** |
| | | * 获取名片模板 |
| | | * @return string |
| | | * @throws \app\common\exception\BaseException |
| | | * @throws \think\exception\DbException |
| | | * @throws \Exception |
| | | */ |
| | | public function getImageE($config, $template_id = '') |
| | | { |
| | | $model = new Template(); |
| | | $this->config = $config; |
| | | // 小程序id |
| | | $appId = $this->template['app_id']; |
| | | // 1. 下载背景图 |
| | | $backdrop = $this->saveTempImage($appId, $this->config['backdrop']['src'], 'backdrop'); |
| | | // 2. 下载用户头像 |
| | | $avatarUrl = $this->saveTempImage($appId, $model::$base_url."/image/agent/avatar.jpg", 'avatar', $template_id); |
| | | // 2. 下载logo |
| | | $logo = $this->saveTempImage($appId, $this->config['logo']['src'], 'logo'); |
| | | // 4. 拼接名片 |
| | | return $this->savePoster($backdrop, $avatarUrl, 'template', $logo); |
| | | } |
| | | |
| | | /** |
| | | * 获取颜色 |
| | | * @param $color |
| | | * @param $editor |
| | | * @return false|int |
| | | */ |
| | | private function colorResource($color, $editor) |
| | | { |
| | | $Color = new Color($color); |
| | | list($r, $g, $b, $alpha) = $Color->getRgba(); |
| | | $scale = round(127 * $alpha); |
| | | $invert = 127 - $scale; |
| | | return imagecolorallocatealpha( |
| | | $editor, |
| | | $r, $g, $b, |
| | | $invert |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | *计算字体大小和顶部跟左边的距离 |
| | | * @param $fontSize |
| | | * @param $left |
| | | * @param $top |
| | | * @return array |
| | | */ |
| | | private function SizeLeftTop($fontSize,$left,$top){ |
| | | $data[0] = $fontSize * 0.76; |
| | | $data[1] = $left; |
| | | $data[2] = $top + $fontSize; |
| | | return $data; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\shop\controller\plus\business; |
| | | use app\shop\controller\Controller; |
| | | use app\shop\model\plus\business\Business as BusinessModel; |
| | | /** |
| | | * 名片管理 |
| | | */ |
| | | class Business extends Controller |
| | | { |
| | | public function index(){ |
| | | $list=(new BusinessModel)->getList(request()->request()); |
| | | return $this->renderSuccess('',compact('list')); |
| | | } |
| | | public function edit(){ |
| | | $param=request()->param();$param=$param['business']; |
| | | $model=(new BusinessModel())->get($param['business_card_id']); |
| | | if($model->add($param)){ |
| | | return $this->renderSuccess('编辑成功'); |
| | | } |
| | | return $this->renderError('编辑失败'); |
| | | } |
| | | public function delete(){ |
| | | $param=request()->param(); |
| | | if((new BusinessModel())->where('business_card_id',$param['business_card_id'])->delete()){ |
| | | return $this->renderSuccess('删除成功'); |
| | | } |
| | | return $this->renderError('删除失败'); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\shop\controller\plus\business; |
| | | |
| | | use app\shop\model\plus\business\Grade as GradeModel; |
| | | use app\shop\controller\Controller; |
| | | |
| | | /** |
| | | * 名片等级控制器 |
| | | */ |
| | | class Grade extends Controller |
| | | { |
| | | /** |
| | | * 名片等级列表 |
| | | */ |
| | | public function index() |
| | | { |
| | | $model = new GradeModel; |
| | | $list = $model->getList($this->postData()); |
| | | return $this->renderSuccess('', compact('list')); |
| | | } |
| | | |
| | | /** |
| | | * 添加等级 |
| | | */ |
| | | public function add() |
| | | { |
| | | $model = new GradeModel; |
| | | // 新增记录 |
| | | if ($model->add($this->postData())) { |
| | | return $this->renderSuccess('添加成功'); |
| | | } |
| | | return $this->renderError($model->getError() ?: '添加失败'); |
| | | } |
| | | |
| | | /** |
| | | * 编辑等级 |
| | | */ |
| | | public function edit($grade_id) |
| | | { |
| | | $model = GradeModel::detail($grade_id); |
| | | if (!$model) { |
| | | return $this->renderError('等级不存在'); |
| | | } |
| | | // 修改记录 |
| | | if ($model->edit($this->postData())) { |
| | | return $this->renderSuccess('修改成功'); |
| | | } |
| | | return $this->renderError($model->getError() ?: '修改失败'); |
| | | } |
| | | |
| | | /** |
| | | * 删除等级 |
| | | */ |
| | | public function delete($grade_id) |
| | | { |
| | | // 等级详情 |
| | | $model = GradeModel::detail($grade_id); |
| | | if (!$model) { |
| | | return $this->renderError('等级不存在'); |
| | | } |
| | | if (!$model->setDelete()) { |
| | | return $this->renderError('已存在使用此等级的名片,删除失败'); |
| | | } |
| | | return $this->renderSuccess('删除成功'); |
| | | } |
| | | |
| | | /** |
| | | * 获取等级详情 |
| | | */ |
| | | public function detail($grade_id) |
| | | { |
| | | $model = GradeModel::detail($grade_id); |
| | | if (!$model) { |
| | | return $this->renderError('等级不存在'); |
| | | } |
| | | return $this->renderSuccess('', compact('model')); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\shop\controller\plus\business; |
| | | |
| | | use app\shop\controller\Controller; |
| | | use app\shop\model\plus\business\Industry as IndustryModel; |
| | | use think\facade\Cache; |
| | | |
| | | class Industry extends Controller |
| | | { |
| | | /** |
| | | * 行业列表 |
| | | */ |
| | | public function index() |
| | | { |
| | | $model = new IndustryModel; |
| | | $list = $model->getALL(); |
| | | return $this->renderSuccess('',compact('list')); |
| | | } |
| | | |
| | | /** |
| | | * 行业详情 |
| | | */ |
| | | public function detail($industry_id) |
| | | { |
| | | $industry = IndustryModel::detail($industry_id); |
| | | if (!$industry) { |
| | | return $this->renderError('行业不存在'); |
| | | } |
| | | return $this->renderSuccess(compact('industry')); |
| | | } |
| | | |
| | | /** |
| | | * 添加行业 |
| | | */ |
| | | public function add() |
| | | { |
| | | $model = new IndustryModel; |
| | | // 获取post数据 |
| | | $data = $this->request->post(); |
| | | // 添加行业 |
| | | if ($model->add($data)) { |
| | | // 清理缓存 |
| | | Cache::tag('cache')->clear(); |
| | | return $this->renderSuccess('添加成功'); |
| | | } |
| | | return $this->renderError($model->getError() ?: '添加失败'); |
| | | } |
| | | |
| | | /** |
| | | * 编辑行业 |
| | | */ |
| | | public function edit($industry_id) |
| | | { |
| | | // 行业详情 |
| | | $industry = IndustryModel::detail($industry_id); |
| | | if (!$industry) { |
| | | return $this->renderError('行业不存在'); |
| | | } |
| | | // 验证表单 |
| | | if (!$this->request->isPost()) { |
| | | return $this->renderError('请求方式错误'); |
| | | } |
| | | // 获取post数据 |
| | | $data = $this->request->post(); |
| | | // 编辑行业 |
| | | if ($industry->edit($data)) { |
| | | // 清理缓存 |
| | | Cache::tag('cache')->clear(); |
| | | return $this->renderSuccess('编辑成功'); |
| | | } |
| | | return $this->renderError($industry->getError() ?: '编辑失败'); |
| | | } |
| | | |
| | | /** |
| | | * 删除行业 |
| | | */ |
| | | public function delete($industry_id) |
| | | { |
| | | // 行业详情 |
| | | $industry = IndustryModel::detail($industry_id); |
| | | if (!$industry) { |
| | | return $this->renderError('行业不存在'); |
| | | } |
| | | // 检查是否有子行业 |
| | | if (IndustryModel::hasSubIndustry($industry_id)) { |
| | | return $this->renderError('该行业下存在子行业,无法删除'); |
| | | } |
| | | // 删除行业 |
| | | if ($industry->delete()) { |
| | | // 清理缓存 |
| | | Cache::tag('cache')->clear(); |
| | | return $this->renderSuccess('删除成功'); |
| | | } |
| | | return $this->renderError('删除失败'); |
| | | } |
| | | |
| | | /** |
| | | * 获取一级行业列表 |
| | | */ |
| | | public function getFirstIndustry() |
| | | { |
| | | $list = IndustryModel::getFirstIndustry(); |
| | | return $this->renderSuccess(compact('list')); |
| | | } |
| | | |
| | | /** |
| | | * 根据上级ID获取子行业 |
| | | */ |
| | | public function getSubIndustry($parent_id = 0) |
| | | { |
| | | $model = new IndustryModel; |
| | | $list = $model->where('parent_id', '=', $parent_id)->order(['sort' => 'asc', 'create_time' => 'asc'])->select(); |
| | | return $this->renderSuccess(compact('list')); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\shop\controller\plus\business; |
| | | |
| | | use app\common\service\business\Poster; |
| | | use app\shop\model\plus\business\Template as BusinessTemplate; |
| | | use app\shop\controller\Controller; |
| | | |
| | | class Template extends Controller |
| | | { |
| | | public function index() |
| | | { |
| | | $list = (new BusinessTemplate())->getList(); |
| | | return $this->renderSuccess('', compact('list')); |
| | | } |
| | | |
| | | public function edit() |
| | | { |
| | | $template_id = input('template_id'); |
| | | $model = (new BusinessTemplate())->where('template_id', $template_id)->find(); |
| | | if (request()->isGet()) { |
| | | $data = json_decode($model['style'], true); |
| | | empty($data['position']) ? $data['position'] = [] : ''; |
| | | empty($data['is_business']) ? $data['is_business'] = 0 : ''; |
| | | empty($data['positionNum']) ? $data['positionNum'] = 0 : ''; |
| | | empty($data['icon']) ? $data['icon'] = [] : ''; |
| | | $data = json_encode($data, JSON_UNESCAPED_UNICODE); |
| | | return $this->renderSuccess('', compact('data')); |
| | | } |
| | | $dealer = ['business_card_id' => $template_id, 'name' => 'XXX', 'unit' => [], 'duties' => [], 'address' => [], 'mobile' => 'xxxxxxxxxxx', 'wechat' => 'xxxxxxxxxxx', 'mailbox' => 'xxxxxxxxxxx@.xxx.com', 'phone' => 'xxx-xxx-xxx', 'website' => 'xxxxxxxxxxxxxx.com', 'fax' => 'xxx-xxx', 'zip_code' => 'xxxxxx', 'template_id' => $template_id, 'wxapp_id' => 10001]; |
| | | $param = request()->param(); |
| | | $imageInfo = getimagesize($param['template']['backdrop']['src']); |
| | | $param['template']['backdrop']['height'] = $imageInfo[1]; |
| | | $param['template']['backdrop']['width'] = $imageInfo[0]; |
| | | foreach ($param['template']['unit'] as $key => $value) { |
| | | // 写入公司 |
| | | $dealer['unit'][] = 'xxxxx公司' . ($key + 1); |
| | | } |
| | | // 写入职位 |
| | | //$dealer['duties'][] = '职位'; |
| | | foreach ($param['template']['duties'] as $key => $value) { |
| | | // 写入职位 |
| | | $dealer['duties'][] = '职位'.($key + 1); |
| | | } |
| | | |
| | | foreach ($param['template']['address'] as $key => $value) { |
| | | // 写入地址 |
| | | $dealer['address'][] = '广西壮族自治区南宁市江南区壮锦大道八桂绿城·龙湖御景-A栋-2单元'.($key + 1).'号'; |
| | | } |
| | | |
| | | $Qrcode = new Poster($dealer); |
| | | $paramL['image'] = $Qrcode->getImageE($param['template'], $template_id); |
| | | $paramL['style'] = json_encode($param['template'], JSON_UNESCAPED_UNICODE); |
| | | if ($model->add($paramL)) { |
| | | return $this->renderSuccess('编辑成功', url('business.template/index')); |
| | | } |
| | | return $this->renderError('编辑失败'); |
| | | } |
| | | |
| | | public function add() |
| | | { |
| | | |
| | | $model = new BusinessTemplate(); |
| | | if (request()->isGet()) { |
| | | $data = ["backdrop" => [ |
| | | "src" => $model::$base_url."/image/agent/agent-bg.jpg", |
| | | 'type' => 'backdrop' |
| | | ], |
| | | "is_business" => 0, |
| | | "name" => [ |
| | | "fontSize" => 14, |
| | | "color" => "#000000", |
| | | "left" => 232, |
| | | "top" => 13, |
| | | "fontWeight" => 400, |
| | | 'type' => 'text' |
| | | ], |
| | | "avatar" => [ |
| | | "width" => 70, |
| | | "style" => "circle", |
| | | "left" => 37, |
| | | "top" => 37, |
| | | "display" => 1, |
| | | "src" => $model::$base_url."/image/agent/avatar.jpg", |
| | | 'type' => 'avatar' |
| | | ], |
| | | "logo" => [ |
| | | "width" => 70, |
| | | "height" => 70, |
| | | "style" => "square", |
| | | "left" => 22, |
| | | "src" => $model::$base_url."/image/diy/logo_top.png", |
| | | "top" => 140, |
| | | "display" => 1, |
| | | 'type' => 'image' |
| | | ], |
| | | "mobile" => [ |
| | | "fontSize" => 14, |
| | | "color" => "#000000", |
| | | "left" => 192, |
| | | "top" => 43, |
| | | "fontWeight" => 400 |
| | | ], |
| | | "address" => [ |
| | | ["fontSize" => 14, |
| | | "color" => "#000000", |
| | | "left" => 133, |
| | | "top" => 206, |
| | | "fontWeight" => 400, |
| | | 'type' => 'text'] |
| | | ], |
| | | "unit" => [ |
| | | ["fontSize" => 14, |
| | | "color" => "#000000", |
| | | "left" => 133, |
| | | "top" => 167, |
| | | "fontWeight" => 100, |
| | | 'type' => 'text'] |
| | | ], |
| | | "duties" => [ |
| | | ["fontSize" => 14, |
| | | "color" => "#000000", |
| | | "left" => 260, |
| | | "top" => 167, |
| | | "fontWeight" => 400, |
| | | 'type' => 'text'] |
| | | ], |
| | | "position" => [ |
| | | ], |
| | | "wechat" => [ |
| | | "fontSize" => 14, |
| | | "color" => "#000000", |
| | | "left" => 205, |
| | | "top" => 65, |
| | | "fontWeight" => 400, |
| | | 'type' => 'text' |
| | | ], |
| | | "mailbox" => [ |
| | | "fontSize" => 14, |
| | | "color" => "#000000", |
| | | "left" => 205, |
| | | "top" => 104, |
| | | "fontWeight" => 400, |
| | | 'type' => 'text' |
| | | ], |
| | | "phone" => [ |
| | | "fontSize" => 14, |
| | | "color" => "#000000", |
| | | "left" => 205, |
| | | "top" => 84, |
| | | "fontWeight" => 400, |
| | | 'type' => 'text' |
| | | ], |
| | | 'positionNum' => 0, |
| | | "iconL" => [], |
| | | |
| | | ]; |
| | | return $this->renderSuccess('', [ |
| | | 'data' => json_encode($data, JSON_UNESCAPED_UNICODE) |
| | | ]); |
| | | } |
| | | $param = request()->param(); |
| | | $imageInfo = getimagesize($param['template']['backdrop']['src']); |
| | | $param['template']['backdrop']['height'] = $imageInfo[1]; |
| | | $param['template']['backdrop']['width'] = $imageInfo[0]; |
| | | $paramL['style'] = json_encode($param['template'], JSON_UNESCAPED_UNICODE); |
| | | if ($model->add($paramL)) { |
| | | $template_id = $model->template_id; |
| | | $dealer = ['business_card_id' => $template_id, 'name' => 'XXX', 'unit' => [], 'duties' => [], 'address' => [], 'mobile' => 'xxxxxxxxxxx', 'wechat' => 'xxxxxxxxxxx', 'mailbox' => 'Xxxxxxxxxxxxxxxxx', 'phone' => 'xxx-xxx-xxx', 'website' => 'xxxxxxxxxxxxxx.com', 'fax' => 'xxx-xxx', 'zip_code' => 'xxxxxx', 'template_id' => $template_id, 'wxapp_id' => 10001]; |
| | | foreach ($param['template']['unit'] as $key => $value) { |
| | | // 写入公司 |
| | | $dealer['unit'][] = 'xxxxx公司' . ($key + 1); |
| | | } |
| | | // 写入职位 |
| | | $dealer['duties'][] = '职位'; |
| | | // 写入地址 |
| | | $dealer['address'][] = '地址1号'; |
| | | $Qrcode = new Poster($dealer); |
| | | $paramI['image'] = $Qrcode->getImageE($param['template'], $template_id); |
| | | $modelBusiness = (new BusinessTemplate())->where(['template_id' => $template_id])->find(); |
| | | $modelBusiness->where(['template_id' => $template_id])->update($paramI); |
| | | return $this->renderSuccess('添加成功', url('business.template/index')); |
| | | } |
| | | return $this->renderError('添加失败'); |
| | | } |
| | | |
| | | public function delete($template_id) |
| | | { |
| | | if ((new BusinessTemplate())->where(['template_id' => $template_id])->delete()) { |
| | | return $this->renderSuccess('删除成功'); |
| | | } |
| | | return $this->renderError('删除失败'); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\shop\model\plus\business; |
| | | use app\common\model\plus\business\Business as BusinessModel; |
| | | |
| | | /** |
| | | * 名片管理模型 |
| | | */ |
| | | class Business extends BusinessModel |
| | | { |
| | | |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\shop\model\plus\business; |
| | | |
| | | use app\common\model\plus\business\Grade as GradeModel; |
| | | |
| | | /** |
| | | * 名片等级模型 |
| | | */ |
| | | class Grade extends GradeModel |
| | | { |
| | | /** |
| | | * 获取列表记录 |
| | | */ |
| | | public function getList($data = []) |
| | | { |
| | | $list = $this->selectList(); |
| | | // 如果为空,则插入默认等级 |
| | | if(count($list) == 0) { |
| | | $this->save([ |
| | | 'name' => '默认等级', |
| | | 'price' => 0.00, |
| | | 'weight' => 1, |
| | | 'app_id' => self::$app_id, |
| | | 'create_time' => time(), |
| | | 'update_time' => time() |
| | | ]); |
| | | $list = $this->selectList(); |
| | | } |
| | | return $list; |
| | | } |
| | | |
| | | /** |
| | | * 新增等级 |
| | | */ |
| | | public function add($data) |
| | | { |
| | | // 验证数据 |
| | | if(empty($data['name'])) { |
| | | $this->error = '等级名称不能为空'; |
| | | return false; |
| | | } |
| | | if(!isset($data['price']) || $data['price'] === '') { |
| | | $data['price'] = 0.00; |
| | | } else { |
| | | $data['price'] = floatval($data['price']); |
| | | if($data['price'] < 0) { |
| | | $this->error = '查看联系方式价格不能小于0'; |
| | | return false; |
| | | } |
| | | } |
| | | if(!isset($data['weight']) || $data['weight'] === '') { |
| | | $data['weight'] = 100; |
| | | } else { |
| | | $data['weight'] = intval($data['weight']); |
| | | if($data['weight'] < 0) { |
| | | $this->error = '权重不能小于0'; |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return parent::add($data); |
| | | } |
| | | |
| | | /** |
| | | * 编辑等级 |
| | | */ |
| | | public function edit($data) |
| | | { |
| | | // 验证数据 |
| | | if(empty($data['name'])) { |
| | | $this->error = '等级名称不能为空'; |
| | | return false; |
| | | } |
| | | if(!isset($data['price']) || $data['price'] === '') { |
| | | $data['price'] = 0.00; |
| | | } else { |
| | | $data['price'] = floatval($data['price']); |
| | | if($data['price'] < 0) { |
| | | $this->error = '查看联系方式价格不能小于0'; |
| | | return false; |
| | | } |
| | | } |
| | | if(!isset($data['weight']) || $data['weight'] === '') { |
| | | $data['weight'] = 100; |
| | | } else { |
| | | $data['weight'] = intval($data['weight']); |
| | | if($data['weight'] < 0) { |
| | | $this->error = '权重不能小于0'; |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return parent::edit($data); |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | namespace app\shop\model\plus\business; |
| | | |
| | | use app\common\model\plus\business\Industry as CommonIndustry; |
| | | use think\facade\Cache; |
| | | |
| | | class Industry extends CommonIndustry |
| | | { |
| | | /** |
| | | * 添加行业 |
| | | */ |
| | | public function add($data) |
| | | { |
| | | // 开启事务 |
| | | $this->startTrans(); |
| | | try { |
| | | // 写入数据 |
| | | $this->save([ |
| | | 'name' => $data['name'], |
| | | 'parent_id' => isset($data['parent_id']) ? $data['parent_id'] : 0, |
| | | 'sort' => isset($data['sort']) ? $data['sort'] : 0, |
| | | 'app_id' => self::$app_id, |
| | | 'create_time' => time(), |
| | | 'update_time' => time(), |
| | | 'status' => 1 |
| | | ]); |
| | | // 提交事务 |
| | | $this->commit(); |
| | | // 清理缓存 |
| | | Cache::tag('cache')->clear(); |
| | | return true; |
| | | } catch (\Exception $e) { |
| | | // 回滚事务 |
| | | $this->rollback(); |
| | | $this->error = $e->getMessage(); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 编辑行业 |
| | | */ |
| | | public function edit($data) |
| | | { |
| | | // 检查是否将自己或子行业设为上级 |
| | | if (isset($data['parent_id']) && $data['parent_id'] > 0) { |
| | | $subIds = $this->getSubIndustryId($this['industry_id']); |
| | | if (in_array($data['parent_id'], $subIds)) { |
| | | $this->error = '不能将自己或子行业设为上级'; |
| | | return false; |
| | | } |
| | | } |
| | | // 开启事务 |
| | | $this->startTrans(); |
| | | try { |
| | | // 更新数据 |
| | | $this->allowField(['name', 'parent_id', 'sort', 'update_time'])->save([ |
| | | 'name' => $data['name'], |
| | | 'parent_id' => isset($data['parent_id']) ? $data['parent_id'] : 0, |
| | | 'sort' => isset($data['sort']) ? $data['sort'] : 0, |
| | | 'update_time' => time(), |
| | | ]); |
| | | // 提交事务 |
| | | $this->commit(); |
| | | // 清理缓存 |
| | | Cache::tag('cache')->clear(); |
| | | return true; |
| | | } catch (\Exception $e) { |
| | | // 回滚事务 |
| | | $this->rollback(); |
| | | $this->error = $e->getMessage(); |
| | | return false; |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | <?php |
| | | |
| | | namespace app\shop\model\plus\business; |
| | | use app\common\model\plus\business\Template as TemplateModel; |
| | | |
| | | /** |
| | | * 名片模板 |
| | | */ |
| | | class Template extends TemplateModel |
| | | { |
| | | |
| | | } |
| New file |
| | |
| | | <template> |
| | | <view> |
| | | <header-bar title="编辑名片" :isBack="true" @click="back"></header-bar> |
| | | <scroll-view scroll-y="true" class="scroll-view"> |
| | | <!-- 名片预览区域 --> |
| | | <view class="preview-section"> |
| | | <view class="preview-title">名片预览</view> |
| | | <view class="preview-card" :style="previewStyle"> |
| | | <image v-if="business.background_image" class="preview-bg" :src="business.background_image" mode="aspectFill"></image> |
| | | <view class="preview-content"> |
| | | <image v-if="file_path" class="preview-avatar" :src="file_path" mode="aspectFill"></image> |
| | | <view class="preview-info"> |
| | | <view class="preview-name">{{business.real_name || '姓名'}}</view> |
| | | <view class="preview-company">{{business.company_name || '公司名称'}}</view> |
| | | <view class="preview-position">{{business.position || '职位'}}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 模板选择 --> |
| | | <view class="template-section"> |
| | | <view class="section-title">选择模板</view> |
| | | <scroll-view scroll-x="true" class="template-scroll"> |
| | | <view class="template-item" v-for="(template, index) in templateList" :key="index" |
| | | :class="{active: template_id === template.template_id}" @click="selectTemplate(index)"> |
| | | <image class="template-img" :src="template.preview_image" mode="aspectFill"></image> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | |
| | | <!-- 表单内容 --> |
| | | <form @submit="submitForm" class="form-section"> |
| | | <!-- 基本信息 --> |
| | | <view class="form-group"> |
| | | <view class="group-title">基本信息</view> |
| | | |
| | | <!-- 头像上传 --> |
| | | <view class="form-item" v-if="avatar_display"> |
| | | <view class="item-label">头像</view> |
| | | <view class="upload-area" @click="uploadImage('avatar')"> |
| | | <image v-if="file_path" class="upload-img" :src="file_path" mode="aspectFill"></image> |
| | | <view v-else class="upload-placeholder"> |
| | | <text class="icon iconfont icon-camera"></text> |
| | | <text>上传头像</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 姓名 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">姓名</view> |
| | | <input type="text" class="item-input" v-model="business.real_name" placeholder="请输入姓名" name="real_name" /> |
| | | </view> |
| | | |
| | | <!-- 公司名称 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">公司名称</view> |
| | | <input type="text" class="item-input" v-model="business.company_name" placeholder="请输入公司名称" name="company_name" /> |
| | | </view> |
| | | |
| | | <!-- 职位 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">职位</view> |
| | | <input type="text" class="item-input" v-model="business.position" placeholder="请输入职位" name="position" /> |
| | | </view> |
| | | |
| | | <!-- 公司Logo --> |
| | | <view class="form-item" v-if="logo_display"> |
| | | <view class="item-label">公司Logo</view> |
| | | <view class="upload-area" @click="uploadImage('logo')"> |
| | | <image v-if="logo_path" class="upload-img" :src="logo_path" mode="aspectFill"></image> |
| | | <view v-else class="upload-placeholder"> |
| | | <text class="icon iconfont icon-camera"></text> |
| | | <text>上传Logo</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 联系方式 --> |
| | | <view class="form-group"> |
| | | <view class="group-title">联系方式</view> |
| | | |
| | | <!-- 手机号 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">手机号</view> |
| | | <input type="number" class="item-input" v-model="business.phone" placeholder="请输入手机号" name="phone" /> |
| | | </view> |
| | | |
| | | <!-- 备用电话 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">备用电话</view> |
| | | <input type="number" class="item-input" v-model="business.mobile" placeholder="请输入备用电话" name="mobile" /> |
| | | </view> |
| | | |
| | | <!-- 邮箱 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">邮箱</view> |
| | | <input type="text" class="item-input" v-model="business.email" placeholder="请输入邮箱" name="email" /> |
| | | </view> |
| | | |
| | | <!-- 地址 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">地址</view> |
| | | <input type="text" class="item-input" v-model="business.address" placeholder="请输入地址" name="address" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 详细信息 --> |
| | | <view class="form-group"> |
| | | <view class="group-title">详细信息</view> |
| | | |
| | | <!-- 个人简介 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">个人简介</view> |
| | | <textarea class="item-textarea" v-model="business.intro" placeholder="请输入个人简介" name="intro" /> |
| | | </view> |
| | | |
| | | <!-- 业务范围 --> |
| | | <view class="form-item"> |
| | | <view class="item-label">业务范围</view> |
| | | <textarea class="item-textarea" v-model="business.business_scope" placeholder="请输入业务范围" name="business_scope" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 保存按钮 --> |
| | | <view class="submit-section"> |
| | | <button form-type="submit" class="submit-btn">保存</button> |
| | | </view> |
| | | </form> |
| | | </scroll-view> |
| | | |
| | | <!-- 上传图片组件 --> |
| | | <Upload v-if="isUpload" @getImgs="handleUpload" @close="closeUpload"></Upload> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import Upload from '@/components/upload/uploadOne.vue'; |
| | | export default { |
| | | components: { |
| | | Upload |
| | | }, |
| | | data() { |
| | | return { |
| | | business: { |
| | | real_name: '', |
| | | company_name: '', |
| | | position: '', |
| | | phone: '', |
| | | mobile: '', |
| | | email: '', |
| | | address: '', |
| | | intro: '', |
| | | business_scope: '', |
| | | background_image: '' |
| | | }, |
| | | templateList: [], |
| | | template_id: '', |
| | | file_id: '', |
| | | file_path: '', |
| | | logo_id: '', |
| | | logo_path: '', |
| | | business_card_id: '', |
| | | avatar_display: true, |
| | | logo_display: true, |
| | | isUpload: false, |
| | | uploadType: '', |
| | | previewStyle: {} |
| | | }; |
| | | }, |
| | | onLoad(options) { |
| | | if (options.business_card_id) { |
| | | this.business_card_id = options.business_card_id; |
| | | this.getBusinessDetail(); |
| | | } |
| | | this.getTemplateList(); |
| | | }, |
| | | methods: { |
| | | back() { |
| | | uni.navigateBack(); |
| | | }, |
| | | // 获取模板列表 |
| | | getTemplateList() { |
| | | let _this = this; |
| | | uni.getSystemInfo({ success: function(res) { _this.systemInfo = res; } }); |
| | | _this._post('plus.business/template/getList', { screenWidth: _this.systemInfo.screenWidth }, function(res) { |
| | | _this.templateList = res.data; |
| | | if (_this.templateList.length > 0 && !_this.template_id) { |
| | | _this.template_id = _this.templateList[0].template_id; |
| | | _this.selectTemplate(0); |
| | | } |
| | | }); |
| | | }, |
| | | // 获取名片详情 |
| | | getBusinessDetail() { |
| | | let _this = this; |
| | | _this._post('plus.business/business/detail', { business_card_id: _this.business_card_id }, function(res) { |
| | | if (res.data) { |
| | | _this.business = res.data; |
| | | _this.template_id = res.data.template_id; |
| | | _this.file_id = res.data.file_id; |
| | | _this.file_path = res.data.file_path || ''; |
| | | _this.logo_id = res.data.logo_id; |
| | | _this.logo_path = res.data.logo_path || ''; |
| | | // 找到对应模板并应用样式 |
| | | _this.templateList.forEach((template, index) => { |
| | | if (template.template_id === _this.template_id) { |
| | | _this.selectTemplate(index); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | // 选择模板 |
| | | selectTemplate(index) { |
| | | const template = this.templateList[index]; |
| | | this.template_id = template.template_id; |
| | | // 应用模板样式 |
| | | this.avatar_display = template.style?.avatar?.display !== false; |
| | | this.logo_display = template.style?.logo?.display !== false; |
| | | this.previewStyle = { |
| | | backgroundColor: template.style?.background?.color || '#37bde6', |
| | | color: template.style?.text?.color || '#fff' |
| | | }; |
| | | // 如果有背景图优先级高于背景色 |
| | | if (template.background_image) { |
| | | this.business.background_image = template.background_image; |
| | | } |
| | | }, |
| | | // 上传图片 |
| | | uploadImage(type) { |
| | | this.uploadType = type; |
| | | this.isUpload = true; |
| | | }, |
| | | // 处理上传结果 |
| | | handleUpload(data) { |
| | | if (data && data.length > 0) { |
| | | const file = data[0]; |
| | | if (this.uploadType === 'avatar') { |
| | | this.file_id = file.file_id; |
| | | this.file_path = file.file_path; |
| | | } else if (this.uploadType === 'logo') { |
| | | this.logo_id = file.file_id; |
| | | this.logo_path = file.file_path; |
| | | } |
| | | } |
| | | this.closeUpload(); |
| | | }, |
| | | // 关闭上传组件 |
| | | closeUpload() { |
| | | this.isUpload = false; |
| | | this.uploadType = ''; |
| | | }, |
| | | // 表单提交 |
| | | submitForm(e) { |
| | | const formData = e.detail.value; |
| | | |
| | | // 表单验证 |
| | | if (!formData.real_name) { |
| | | this.showError('请输入姓名'); |
| | | return false; |
| | | } |
| | | if (!formData.phone) { |
| | | this.showError('请输入手机号'); |
| | | return false; |
| | | } |
| | | |
| | | // 组装提交数据 |
| | | const submitData = { |
| | | ...formData, |
| | | template_id: this.template_id, |
| | | file_id: this.file_id, |
| | | logo_id: this.logo_id |
| | | }; |
| | | |
| | | // 判断是新增还是编辑 |
| | | let url = 'plus.business/business/add'; |
| | | if (this.business_card_id) { |
| | | url = 'plus.business/business/edit'; |
| | | submitData.business_card_id = this.business_card_id; |
| | | } |
| | | |
| | | // 提交表单 |
| | | let _this = this; |
| | | _this._post(url, submitData, function(res) { |
| | | _this.showSuccess(res.msg, function() { |
| | | uni.navigateBack(); |
| | | }); |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .scroll-view { |
| | | height: calc(100vh - 80rpx); |
| | | } |
| | | |
| | | .preview-section { |
| | | background: #fff; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .preview-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .preview-card { |
| | | height: 400rpx; |
| | | border-radius: 20rpx; |
| | | overflow: hidden; |
| | | position: relative; |
| | | |
| | | .preview-bg { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .preview-content { |
| | | position: relative; |
| | | z-index: 2; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 40rpx; |
| | | color: #fff; |
| | | |
| | | .preview-avatar { |
| | | width: 150rpx; |
| | | height: 150rpx; |
| | | border-radius: 50%; |
| | | border: 4rpx solid #fff; |
| | | } |
| | | |
| | | .preview-info { |
| | | margin-left: 30rpx; |
| | | |
| | | .preview-name { |
| | | font-size: 44rpx; |
| | | font-weight: bold; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .preview-company { |
| | | font-size: 32rpx; |
| | | margin-bottom: 8rpx; |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | .preview-position { |
| | | font-size: 28rpx; |
| | | opacity: 0.8; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .template-section { |
| | | background: #fff; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .section-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .template-scroll { |
| | | white-space: nowrap; |
| | | padding-bottom: 10rpx; |
| | | |
| | | .template-item { |
| | | display: inline-block; |
| | | width: 200rpx; |
| | | height: 140rpx; |
| | | margin-right: 20rpx; |
| | | border-radius: 10rpx; |
| | | overflow: hidden; |
| | | border: 2rpx solid transparent; |
| | | |
| | | &.active { |
| | | border-color: #37bde6; |
| | | } |
| | | |
| | | .template-img { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .form-section { |
| | | background: #fff; |
| | | padding: 30rpx; |
| | | |
| | | .form-group { |
| | | margin-bottom: 40rpx; |
| | | |
| | | .group-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .form-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20rpx 0; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | .item-label { |
| | | width: 160rpx; |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | } |
| | | |
| | | .item-input { |
| | | flex: 1; |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | padding: 0; |
| | | } |
| | | |
| | | .item-textarea { |
| | | flex: 1; |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | padding: 0; |
| | | height: 150rpx; |
| | | text-align: left; |
| | | } |
| | | |
| | | .upload-area { |
| | | width: 150rpx; |
| | | height: 150rpx; |
| | | border-radius: 10rpx; |
| | | overflow: hidden; |
| | | background: #f5f5f5; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | .upload-img { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .upload-placeholder { |
| | | text-align: center; |
| | | |
| | | .icon { |
| | | font-size: 48rpx; |
| | | color: #999; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | text { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .submit-section { |
| | | margin-top: 60rpx; |
| | | margin-bottom: 40rpx; |
| | | |
| | | .submit-btn { |
| | | width: 100%; |
| | | background: #37bde6; |
| | | color: #fff; |
| | | border: none; |
| | | border-radius: 10rpx; |
| | | padding: 28rpx 0; |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view class="visit-card"> |
| | | <view class="card-header"> |
| | | <image :src="visitInfo.avatar" mode="aspectFill" class="avatar"></image> |
| | | <view class="header-info"> |
| | | <view class="visitor-name">{{visitInfo.name || '游客'}}</view> |
| | | <view class="visit-time">{{formatTime(visitInfo.visit_time)}}</view> |
| | | </view> |
| | | </view> |
| | | <view class="card-content"> |
| | | <view class="info-row" v-if="visitInfo.visit_count > 1"> |
| | | <text class="info-label">访问次数:</text> |
| | | <text class="info-value">{{visitInfo.visit_count}}次</text> |
| | | </view> |
| | | <view class="info-row" v-if="visitInfo.device"> |
| | | <text class="info-label">使用设备:</text> |
| | | <text class="info-value">{{visitInfo.device}}</text> |
| | | </view> |
| | | <view class="info-row" v-if="visitInfo.region"> |
| | | <text class="info-label">所在地区:</text> |
| | | <text class="info-value">{{visitInfo.region}}</text> |
| | | </view> |
| | | <view class="info-row" v-if="visitInfo.source"> |
| | | <text class="info-label">来源渠道:</text> |
| | | <text class="info-value">{{visitInfo.source}}</text> |
| | | </view> |
| | | <view class="action-buttons" v-if="showActions"> |
| | | <button class="btn-primary" @click="viewCard">查看名片</button> |
| | | <button class="btn-secondary" @click="contactVisitor">联系访客</button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | props: { |
| | | visitInfo: { |
| | | type: Object, |
| | | default: () => ({ |
| | | avatar: '', |
| | | name: '', |
| | | visit_time: '', |
| | | visit_count: 1, |
| | | device: '', |
| | | region: '', |
| | | source: '' |
| | | }) |
| | | }, |
| | | showActions: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | methods: { |
| | | // 格式化时间 |
| | | formatTime(timeStr) { |
| | | if (!timeStr) return ''; |
| | | const date = new Date(timeStr); |
| | | const now = new Date(); |
| | | const diff = now - date; |
| | | const days = Math.floor(diff / (1000 * 60 * 60 * 24)); |
| | | |
| | | if (days === 0) { |
| | | const hours = Math.floor(diff / (1000 * 60 * 60)); |
| | | if (hours === 0) { |
| | | const minutes = Math.floor(diff / (1000 * 60)); |
| | | return minutes <= 1 ? '刚刚' : minutes + '分钟前'; |
| | | } else { |
| | | return hours + '小时前'; |
| | | } |
| | | } else if (days === 1) { |
| | | return '昨天'; |
| | | } else if (days < 7) { |
| | | return days + '天前'; |
| | | } else { |
| | | return date.getMonth() + 1 + '月' + date.getDate() + '日'; |
| | | } |
| | | }, |
| | | // 查看访客名片 |
| | | viewCard() { |
| | | if (this.visitInfo.user_id) { |
| | | this.$emit('viewCard', this.visitInfo.user_id); |
| | | } |
| | | }, |
| | | // 联系访客 |
| | | contactVisitor() { |
| | | this.$emit('contact', this.visitInfo); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .visit-card { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 20rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | align-items: center; |
| | | padding-bottom: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | .avatar { |
| | | width: 80rpx; |
| | | height: 80rpx; |
| | | border-radius: 40rpx; |
| | | margin-right: 20rpx; |
| | | } |
| | | |
| | | .header-info { |
| | | flex: 1; |
| | | |
| | | .visitor-name { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 5rpx; |
| | | } |
| | | |
| | | .visit-time { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .card-content { |
| | | padding-top: 20rpx; |
| | | |
| | | .info-row { |
| | | display: flex; |
| | | margin-bottom: 15rpx; |
| | | |
| | | .info-label { |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | margin-right: 10rpx; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | flex: 1; |
| | | } |
| | | } |
| | | |
| | | .action-buttons { |
| | | display: flex; |
| | | margin-top: 20rpx; |
| | | |
| | | button { |
| | | flex: 1; |
| | | height: 70rpx; |
| | | line-height: 70rpx; |
| | | font-size: 28rpx; |
| | | border-radius: 35rpx; |
| | | margin: 0 10rpx; |
| | | } |
| | | |
| | | .btn-primary { |
| | | background: #37bde6; |
| | | color: #fff; |
| | | } |
| | | |
| | | .btn-secondary { |
| | | background: #f5f5f5; |
| | | color: #666; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view> |
| | | <header-bar title="名片详情" :isBack="true" @click="back"></header-bar> |
| | | <scroll-view scroll-y="true" class="scroll-view"> |
| | | <view class="card-container" v-if="businessInfo"> |
| | | <!-- 名片头部 --> |
| | | <view class="card-header" :style="{backgroundColor: backgroundColor}"> |
| | | <image v-if="businessInfo.background_image" class="card-bg" :src="businessInfo.background_image" mode="aspectFill"></image> |
| | | <view class="header-content"> |
| | | <image class="avatar" :src="businessInfo.avatar || '/static/default.png'" mode="aspectFill"></image> |
| | | <view class="user-info"> |
| | | <view class="name">{{businessInfo.real_name}}</view> |
| | | <view class="company">{{businessInfo.company_name}}</view> |
| | | <view class="position">{{businessInfo.position}}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 公司Logo --> |
| | | <view class="logo-section" v-if="businessInfo.logo_image"> |
| | | <image class="logo" :src="businessInfo.logo_image.file_path" mode="aspectFit"></image> |
| | | </view> |
| | | |
| | | <!-- 联系信息 --> |
| | | <view class="contact-section"> |
| | | <view class="section-title">联系方式</view> |
| | | |
| | | <view class="contact-item" @click="makePhoneCall(businessInfo.phone)"> |
| | | <view class="item-left"> |
| | | <view class="icon-circle"> |
| | | <text class="icon iconfont icon-phone"></text> |
| | | </view> |
| | | <text class="item-label">电话</text> |
| | | </view> |
| | | <view class="item-right"> |
| | | <text class="item-value">{{businessInfo.phone}}</text> |
| | | <text class="icon iconfont icon-jiantou"></text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="contact-item" v-if="businessInfo.mobile" @click="makePhoneCall(businessInfo.mobile)"> |
| | | <view class="item-left"> |
| | | <view class="icon-circle"> |
| | | <text class="icon iconfont icon-mobile"></text> |
| | | </view> |
| | | <text class="item-label">手机</text> |
| | | </view> |
| | | <view class="item-right"> |
| | | <text class="item-value">{{businessInfo.mobile}}</text> |
| | | <text class="icon iconfont icon-jiantou"></text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="contact-item" v-if="businessInfo.email" @click="copyEmail(businessInfo.email)"> |
| | | <view class="item-left"> |
| | | <view class="icon-circle"> |
| | | <text class="icon iconfont icon-email"></text> |
| | | </view> |
| | | <text class="item-label">邮箱</text> |
| | | </view> |
| | | <view class="item-right"> |
| | | <text class="item-value">{{businessInfo.email}}</text> |
| | | <text class="icon iconfont icon-jiantou"></text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="contact-item" v-if="businessInfo.address" @click="openLocation(businessInfo.address)"> |
| | | <view class="item-left"> |
| | | <view class="icon-circle"> |
| | | <text class="icon iconfont icon-location"></text> |
| | | </view> |
| | | <text class="item-label">地址</text> |
| | | </view> |
| | | <view class="item-right"> |
| | | <text class="item-value">{{businessInfo.address}}</text> |
| | | <text class="icon iconfont icon-jiantou"></text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 详细信息 --> |
| | | <view class="detail-section"> |
| | | <view class="section-title">详细信息</view> |
| | | |
| | | <view class="detail-item" v-if="businessInfo.intro"> |
| | | <view class="detail-label">个人简介</view> |
| | | <view class="detail-content">{{businessInfo.intro}}</view> |
| | | </view> |
| | | |
| | | <view class="detail-item" v-if="businessInfo.business_scope"> |
| | | <view class="detail-label">业务范围</view> |
| | | <view class="detail-content">{{businessInfo.business_scope}}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 底部操作按钮 --> |
| | | <view class="action-section"> |
| | | <view class="action-left"> |
| | | <view class="action-btn" @click="saveCard" :class="{active: isSaved}"> |
| | | <text class="icon iconfont" :class="isSaved ? 'icon-success' : 'icon-star'"></text> |
| | | <text>{{isSaved ? '已保存' : '收藏'}}</text> |
| | | </view> |
| | | </view> |
| | | <view class="action-right"> |
| | | <view class="share-btn" @click="shareCard">分享名片</view> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | businessInfo: null, |
| | | business_card_id: '', |
| | | isSaved: false, |
| | | backgroundColor: '#37bde6', |
| | | loading: true |
| | | }; |
| | | }, |
| | | onLoad(options) { |
| | | if (options.business_card_id) { |
| | | this.business_card_id = options.business_card_id; |
| | | this.getBusinessDetail(); |
| | | this.checkSavedStatus(); |
| | | // 记录访问日志 |
| | | this.recordVisit(); |
| | | } |
| | | }, |
| | | methods: { |
| | | back() { |
| | | uni.navigateBack(); |
| | | }, |
| | | // 获取名片详情 |
| | | getBusinessDetail() { |
| | | let _this = this; |
| | | _this.loading = true; |
| | | _this._post('plus.business/business/detail', { business_card_id: _this.business_card_id }, function(res) { |
| | | _this.businessInfo = res.data; |
| | | // 设置背景色 |
| | | if (_this.businessInfo.template && _this.businessInfo.template.style?.background?.color) { |
| | | _this.backgroundColor = _this.businessInfo.template.style.background.color; |
| | | } |
| | | _this.loading = false; |
| | | }); |
| | | }, |
| | | // 检查是否已保存 |
| | | checkSavedStatus() { |
| | | let _this = this; |
| | | _this._post('plus.business/saving/check', { business_card_id: _this.business_card_id }, function(res) { |
| | | _this.isSaved = res.data.is_saved; |
| | | }); |
| | | }, |
| | | // 记录访问 |
| | | recordVisit() { |
| | | let _this = this; |
| | | const params = { |
| | | business_card_id: _this.business_card_id |
| | | }; |
| | | // 如果有推荐人ID,也记录下来 |
| | | if (this.$route.query.referee_id) { |
| | | params.referee_id = this.$route.query.referee_id; |
| | | } |
| | | _this._post('plus.business/business/recordVisit', params, function() { |
| | | // 无需处理返回结果 |
| | | }); |
| | | }, |
| | | // 拨打电话 |
| | | makePhoneCall(phone) { |
| | | uni.makePhoneCall({ |
| | | phoneNumber: phone |
| | | }); |
| | | }, |
| | | // 复制邮箱 |
| | | copyEmail(email) { |
| | | uni.setClipboardData({ |
| | | data: email, |
| | | success: () => { |
| | | this.showSuccess('邮箱已复制'); |
| | | } |
| | | }); |
| | | }, |
| | | // 打开地图 |
| | | openLocation(address) { |
| | | // 这里简化处理,实际项目中可能需要调用地图API进行地理编码 |
| | | uni.openLocation({ |
| | | latitude: 0, |
| | | longitude: 0, |
| | | name: address, |
| | | address: address, |
| | | scale: 18 |
| | | }); |
| | | }, |
| | | // 保存名片 |
| | | saveCard() { |
| | | let _this = this; |
| | | _this._post('plus.business/saving/save', { business_card_id: _this.business_card_id }, function(res) { |
| | | _this.isSaved = !_this.isSaved; |
| | | _this.showSuccess(_this.isSaved ? '保存成功' : '取消保存'); |
| | | }); |
| | | }, |
| | | // 分享名片 |
| | | shareCard() { |
| | | uni.showShareMenu({ |
| | | withShareTicket: true, |
| | | menus: ['shareAppMessage', 'shareTimeline'] |
| | | }); |
| | | } |
| | | }, |
| | | onShareAppMessage() { |
| | | if (this.businessInfo) { |
| | | return { |
| | | title: `${this.businessInfo.real_name}的电子名片`, |
| | | path: `/pages/plus/business/detail?business_card_id=${this.business_card_id}&referee_id=${this.getUserId()}` |
| | | }; |
| | | } |
| | | return { |
| | | title: '电子名片', |
| | | path: `/pages/plus/business/detail?business_card_id=${this.business_card_id}` |
| | | }; |
| | | }, |
| | | onShareTimeline() { |
| | | if (this.businessInfo) { |
| | | return { |
| | | title: `${this.businessInfo.real_name}的电子名片`, |
| | | path: `/pages/plus/business/detail?business_card_id=${this.business_card_id}&referee_id=${this.getUserId()}` |
| | | }; |
| | | } |
| | | return { |
| | | title: '电子名片', |
| | | path: `/pages/plus/business/detail?business_card_id=${this.business_card_id}` |
| | | }; |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .scroll-view { |
| | | height: calc(100vh - 80rpx); |
| | | } |
| | | |
| | | .card-header { |
| | | position: relative; |
| | | color: #fff; |
| | | padding: 40rpx; |
| | | |
| | | .card-bg { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | z-index: 1; |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | .header-content { |
| | | position: relative; |
| | | z-index: 2; |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .avatar { |
| | | width: 180rpx; |
| | | height: 180rpx; |
| | | border-radius: 50%; |
| | | border: 6rpx solid rgba(255, 255, 255, 0.8); |
| | | background: #fff; |
| | | } |
| | | |
| | | .user-info { |
| | | margin-left: 30rpx; |
| | | |
| | | .name { |
| | | font-size: 48rpx; |
| | | font-weight: bold; |
| | | margin-bottom: 12rpx; |
| | | } |
| | | |
| | | .company { |
| | | font-size: 34rpx; |
| | | margin-bottom: 8rpx; |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | .position { |
| | | font-size: 30rpx; |
| | | opacity: 0.8; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .logo-section { |
| | | background: #fff; |
| | | padding: 30rpx 0; |
| | | display: flex; |
| | | justify-content: center; |
| | | |
| | | .logo { |
| | | width: 150rpx; |
| | | height: 150rpx; |
| | | border-radius: 10rpx; |
| | | } |
| | | } |
| | | |
| | | .contact-section, |
| | | .detail-section { |
| | | background: #fff; |
| | | margin-top: 20rpx; |
| | | padding: 30rpx; |
| | | |
| | | .section-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 20rpx; |
| | | padding-bottom: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | } |
| | | |
| | | .contact-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 25rpx 0; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .icon-circle { |
| | | width: 60rpx; |
| | | height: 60rpx; |
| | | border-radius: 50%; |
| | | background: #f5f5f5; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 20rpx; |
| | | |
| | | .icon { |
| | | font-size: 36rpx; |
| | | color: #37bde6; |
| | | } |
| | | } |
| | | |
| | | .item-label { |
| | | font-size: 30rpx; |
| | | color: #666; |
| | | } |
| | | } |
| | | |
| | | .item-right { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .item-value { |
| | | font-size: 30rpx; |
| | | color: #333; |
| | | margin-right: 10rpx; |
| | | } |
| | | |
| | | .icon { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .detail-item { |
| | | margin-bottom: 30rpx; |
| | | |
| | | .detail-label { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #666; |
| | | margin-bottom: 15rpx; |
| | | } |
| | | |
| | | .detail-content { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | line-height: 1.6; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .action-section { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | background: #fff; |
| | | box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.1); |
| | | display: flex; |
| | | padding: 20rpx; |
| | | z-index: 999; |
| | | |
| | | .action-left { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .action-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 15rpx 30rpx; |
| | | border-radius: 30rpx; |
| | | border: 2rpx solid #37bde6; |
| | | color: #37bde6; |
| | | |
| | | &.active { |
| | | background: #37bde6; |
| | | color: #fff; |
| | | } |
| | | |
| | | .icon { |
| | | font-size: 28rpx; |
| | | margin-right: 10rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .action-right { |
| | | flex: 2; |
| | | |
| | | .share-btn { |
| | | width: 100%; |
| | | background: linear-gradient(90deg, #44bbff, #2b81ff); |
| | | color: #fff; |
| | | text-align: center; |
| | | padding: 24rpx 0; |
| | | border-radius: 30rpx; |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view> |
| | | <header-bar title="我的名片" :isBack="true" @click="back"></header-bar> |
| | | <!-- 名片展示区域 --> |
| | | <view class="content"> |
| | | <view class="business-card" v-if="businessList.length > 0"> |
| | | <image class="top-image" :src="businessList[current].background_image" mode="aspectFill"></image> |
| | | <view class="business-info"> |
| | | <image class="avatar" :src="businessList[current].avatar" mode="aspectFill"></image> |
| | | <view class="info-text"> |
| | | <view class="name">{{businessList[current].real_name}}</view> |
| | | <view class="company">{{businessList[current].company_name}}</view> |
| | | <view class="position">{{businessList[current].position}}</view> |
| | | </view> |
| | | </view> |
| | | <view class="business-contact"> |
| | | <view class="contact-item" @click="makePhoneCall(businessList[current].phone)"> |
| | | <text class="icon iconfont icon-phone"></text> |
| | | <text class="text">{{businessList[current].phone}}</text> |
| | | </view> |
| | | <view class="contact-item" v-if="businessList[current].mobile" |
| | | @click="makePhoneCall(businessList[current].mobile)"> |
| | | <text class="icon iconfont icon-mobile"></text> |
| | | <text class="text">{{businessList[current].mobile}}</text> |
| | | </view> |
| | | <view class="contact-item" v-if="businessList[current].email" |
| | | @click="sendEmail(businessList[current].email)"> |
| | | <text class="icon iconfont icon-email"></text> |
| | | <text class="text">{{businessList[current].email}}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 名片操作按钮 --> |
| | | <view class="action-buttons"> |
| | | <view class="btn" @click="editCard()">编辑名片</view> |
| | | <view class="btn" @click="switchCard()">切换名片</view> |
| | | <view class="btn" @click="shareCard()">分享名片</view> |
| | | </view> |
| | | |
| | | <!-- 数据统计区域 --> |
| | | <view class="statistics"> |
| | | <view class="stat-item"> |
| | | <view class="number">{{statistics.view_count || 0}}</view> |
| | | <view class="label">浏览次数</view> |
| | | </view> |
| | | <view class="stat-item"> |
| | | <view class="number">{{statistics.save_count || 0}}</view> |
| | | <view class="label">保存次数</view> |
| | | </view> |
| | | <view class="stat-item"> |
| | | <view class="number">{{statistics.share_count || 0}}</view> |
| | | <view class="label">分享次数</view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 最近访客 --> |
| | | <view class="visitors"> |
| | | <view class="section-title"> |
| | | <text>最近访客</text> |
| | | <text class="more" @click="viewAllVisitors()">查看全部</text> |
| | | </view> |
| | | <view class="visitor-list"> |
| | | <view class="visitor-item" v-for="(visitor, index) in visitors" :key="index"> |
| | | <image class="visitor-avatar" :src="visitor.avatar || '/static/default.png'" mode="aspectFill"> |
| | | </image> |
| | | <view class="visitor-info"> |
| | | <view class="visitor-name">{{visitor.user_name || '未知访客'}}</view> |
| | | <view class="visitor-time">{{formatTime(visitor.visit_time)}}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 切换名片弹窗 --> |
| | | <uni-popup ref="popup" type="bottom" :mask-click="false"> |
| | | <view class="popup-content"> |
| | | <view class="popup-header"> |
| | | <text class="title">选择名片</text> |
| | | <text class="close" @click="closePopup">×</text> |
| | | </view> |
| | | <scroll-view scroll-y="true" class="card-scroll"> |
| | | <view class="card-item" v-for="(card, index) in businessList" :key="index" |
| | | :class="{active: index === current}" @click="selectCard(index)"> |
| | | <view class="card-preview"> |
| | | <view class="card-name">{{card.real_name}}</view> |
| | | <view class="card-company">{{card.company_name}}</view> |
| | | </view> |
| | | <text v-if="index === current" class="icon iconfont icon-check"></text> |
| | | </view> |
| | | <view class="add-card" @click="addNewCard()"> |
| | | <text class="icon iconfont icon-add"></text> |
| | | <text>添加新名片</text> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </uni-popup> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | businessList: [], |
| | | current: 0, |
| | | statistics: {}, |
| | | visitors: [], |
| | | loading: false, |
| | | page: 1, |
| | | search: '' |
| | | }; |
| | | }, |
| | | onLoad() { |
| | | this.init(); |
| | | }, |
| | | onShow() { |
| | | this.getbusinessList(); |
| | | this.getVisitorList(); |
| | | }, |
| | | methods: { |
| | | back() { |
| | | uni.navigateBack(); |
| | | }, |
| | | init() { |
| | | this.getbusinessList(); |
| | | this.getStatistics(); |
| | | this.getVisitorList(); |
| | | }, |
| | | getbusinessList() { |
| | | let _this = this; |
| | | _this._post('plus.business/business/getList', {}, function(res) { |
| | | _this.businessList = res.data; |
| | | if (_this.businessList.length > 0) { |
| | | _this.getCardStatistics(_this.businessList[_this.current].business_card_id); |
| | | } |
| | | }); |
| | | }, |
| | | getCardStatistics(business_card_id) { |
| | | let _this = this; |
| | | _this._post('plus.business/business/getStatistics', { |
| | | business_card_id: business_card_id |
| | | }, function(res) { |
| | | _this.statistics = res.data; |
| | | }); |
| | | }, |
| | | getStatistics() { |
| | | // 获取统计数据 |
| | | let _this = this; |
| | | _this._post('plus.business/business/getStatistics', {}, function(res) { |
| | | _this.statistics = res.data; |
| | | }); |
| | | }, |
| | | getVisitorList() { |
| | | let _this = this; |
| | | _this._post('plus.business/business/getVisitors', { |
| | | page: 1, |
| | | list_rows: 10 |
| | | }, function(res) { |
| | | _this.visitors = res.data.list; |
| | | }); |
| | | }, |
| | | makePhoneCall(phone) { |
| | | uni.makePhoneCall({ |
| | | phoneNumber: phone |
| | | }); |
| | | }, |
| | | sendEmail(email) { |
| | | uni.setClipboardData({ |
| | | data: email, |
| | | success: () => { |
| | | this.showSuccess('邮箱已复制'); |
| | | } |
| | | }); |
| | | }, |
| | | editCard() { |
| | | if (this.businessList.length > 0) { |
| | | this.gotoPage( |
| | | `/pages/plus/business/add?business_card_id=${this.businessList[this.current].business_card_id}` |
| | | ); |
| | | } |
| | | }, |
| | | switchCard() { |
| | | this.$refs.popup.open(); |
| | | }, |
| | | closePopup() { |
| | | this.$refs.popup.close(); |
| | | }, |
| | | selectCard(index) { |
| | | this.current = index; |
| | | this.getCardStatistics(this.businessList[this.current].business_card_id); |
| | | this.closePopup(); |
| | | }, |
| | | addNewCard() { |
| | | this.closePopup(); |
| | | this.gotoPage('/pages/plus/business/add'); |
| | | }, |
| | | shareCard() { |
| | | // 分享名片逻辑 |
| | | if (this.businessList.length > 0) { |
| | | uni.showShareMenu({ |
| | | withShareTicket: true, |
| | | menus: ['shareAppMessage', 'shareTimeline'] |
| | | }); |
| | | } |
| | | }, |
| | | viewAllVisitors() { |
| | | // 查看全部访客 |
| | | this.gotoPage('/pages/plus/business/visitors'); |
| | | }, |
| | | formatTime(time) { |
| | | if (!time) return ''; |
| | | const date = new Date(time); |
| | | const now = new Date(); |
| | | const diff = now - date; |
| | | const days = Math.floor(diff / (1000 * 60 * 60 * 24)); |
| | | if (days === 0) { |
| | | return '今天 ' + date.getHours() + ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); |
| | | } else if (days === 1) { |
| | | return '昨天 ' + date.getHours() + ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); |
| | | } else if (days < 7) { |
| | | return days + '天前'; |
| | | } else { |
| | | return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(); |
| | | } |
| | | } |
| | | }, |
| | | onShareAppMessage() { |
| | | if (this.businessList.length > 0) { |
| | | return { |
| | | title: `${this.businessList[this.current].real_name}的电子名片`, |
| | | path: `/pages/plus/business/detail?business_card_id=${this.businessList[this.current].business_card_id}&referee_id=${this.getUserId()}` |
| | | }; |
| | | } |
| | | return { |
| | | title: '电子名片', |
| | | path: '/pages/plus/business/index' |
| | | }; |
| | | }, |
| | | onShareTimeline() { |
| | | if (this.businessList.length > 0) { |
| | | return { |
| | | title: `${this.businessList[this.current].real_name}的电子名片`, |
| | | path: `/pages/plus/business/detail?business_card_id=${this.businessList[this.current].business_card_id}&referee_id=${this.getUserId()}` |
| | | }; |
| | | } |
| | | return { |
| | | title: '电子名片', |
| | | path: '/pages/plus/business/index' |
| | | }; |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .content { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .business-card { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | overflow: hidden; |
| | | padding-bottom: 30rpx; |
| | | |
| | | .top-image { |
| | | width: 100%; |
| | | height: 300rpx; |
| | | display: block; |
| | | } |
| | | |
| | | .business-info { |
| | | padding: 20rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .avatar { |
| | | width: 150rpx; |
| | | height: 150rpx; |
| | | border-radius: 50%; |
| | | border: 4rpx solid #fff; |
| | | box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .info-text { |
| | | margin-left: 20rpx; |
| | | flex: 1; |
| | | |
| | | .name { |
| | | font-size: 40rpx; |
| | | font-weight: bold; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .company { |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | margin-bottom: 4rpx; |
| | | } |
| | | |
| | | .position { |
| | | font-size: 26rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .business-contact { |
| | | padding: 0 20rpx; |
| | | |
| | | .contact-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 16rpx 0; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | .icon { |
| | | font-size: 32rpx; |
| | | color: #37bde6; |
| | | margin-right: 16rpx; |
| | | } |
| | | |
| | | .text { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .action-buttons { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin: 30rpx 0; |
| | | |
| | | .btn { |
| | | flex: 1; |
| | | background: #37bde6; |
| | | color: #fff; |
| | | text-align: center; |
| | | padding: 24rpx 0; |
| | | border-radius: 10rpx; |
| | | font-size: 30rpx; |
| | | margin: 0 10rpx; |
| | | } |
| | | } |
| | | |
| | | .statistics { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 30rpx 0; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | |
| | | .stat-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .number { |
| | | font-size: 40rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .label { |
| | | font-size: 26rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .visitors { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | margin-top: 30rpx; |
| | | padding: 20rpx; |
| | | |
| | | .section-title { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .more { |
| | | font-size: 26rpx; |
| | | color: #37bde6; |
| | | } |
| | | } |
| | | |
| | | .visitor-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 20rpx 0; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .visitor-avatar { |
| | | width: 80rpx; |
| | | height: 80rpx; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .visitor-info { |
| | | margin-left: 20rpx; |
| | | flex: 1; |
| | | |
| | | .visitor-name { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | margin-bottom: 4rpx; |
| | | } |
| | | |
| | | .visitor-time { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .popup-content { |
| | | background: #fff; |
| | | border-top-left-radius: 30rpx; |
| | | border-top-right-radius: 30rpx; |
| | | |
| | | .popup-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 20rpx 30rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | .title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .close { |
| | | font-size: 40rpx; |
| | | color: #999; |
| | | padding: 0 20rpx; |
| | | } |
| | | } |
| | | |
| | | .card-scroll { |
| | | height: 500rpx; |
| | | |
| | | .card-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 30rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &.active { |
| | | background: #f5f5f5; |
| | | } |
| | | |
| | | .card-preview { |
| | | .card-name { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .card-company { |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | } |
| | | } |
| | | |
| | | .icon { |
| | | font-size: 32rpx; |
| | | color: #37bde6; |
| | | } |
| | | } |
| | | |
| | | .add-card { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 30rpx; |
| | | color: #37bde6; |
| | | font-size: 30rpx; |
| | | |
| | | .icon { |
| | | font-size: 36rpx; |
| | | margin-right: 10rpx; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view> |
| | | <header-bar title="数据统计" :isBack="true" @click="back"></header-bar> |
| | | <view class="content"> |
| | | <!-- 数据概览 --> |
| | | <view class="overview-section"> |
| | | <view class="overview-item"> |
| | | <view class="number">{{totalViews}}</view> |
| | | <view class="label">总浏览量</view> |
| | | </view> |
| | | <view class="overview-item"> |
| | | <view class="number">{{totalSaves}}</view> |
| | | <view class="label">总保存量</view> |
| | | </view> |
| | | <view class="overview-item"> |
| | | <view class="number">{{totalShares}}</view> |
| | | <view class="label">总分享量</view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 趋势图表区域 --> |
| | | <view class="chart-section"> |
| | | <view class="chart-header"> |
| | | <view class="chart-title">数据趋势</view> |
| | | <view class="chart-tabs"> |
| | | <view class="tab" :class="{active: timeRange === 'week'}" @click="setTimeRange('week')">周</view> |
| | | <view class="tab" :class="{active: timeRange === 'month'}" @click="setTimeRange('month')">月</view> |
| | | <view class="tab" :class="{active: timeRange === 'year'}" @click="setTimeRange('year')">年</view> |
| | | </view> |
| | | </view> |
| | | <view class="chart-content"> |
| | | <!-- 这里可以集成图表组件,如echarts等 --> |
| | | <view class="chart-placeholder" v-if="!chartData.length"> |
| | | <text>暂无数据</text> |
| | | </view> |
| | | <view class="chart-list" v-else> |
| | | <view class="chart-item" v-for="(item, index) in chartData" :key="index"> |
| | | <view class="chart-bar"> |
| | | <view class="bar" :style="{height: getBarHeight(item.views)}"> |
| | | <view class="bar-value">{{item.views}}</view> |
| | | </view> |
| | | </view> |
| | | <view class="chart-label">{{item.date}}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 详细统计数据 --> |
| | | <view class="detail-section"> |
| | | <view class="section-title">详细统计</view> |
| | | |
| | | <!-- 访问来源 --> |
| | | <view class="stat-card"> |
| | | <view class="card-title">访问来源</view> |
| | | <view class="source-list"> |
| | | <view class="source-item" v-for="(source, index) in visitSources" :key="index"> |
| | | <view class="source-name">{{source.name}}</view> |
| | | <view class="source-value">{{source.value}}次</view> |
| | | <view class="source-progress"> |
| | | <view class="progress-bar" :style="{width: source.percentage + '%'}"></view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 地域分布 --> |
| | | <view class="stat-card"> |
| | | <view class="card-title">地域分布</view> |
| | | <view class="region-list"> |
| | | <view class="region-item" v-for="(region, index) in regions" :key="index"> |
| | | <view class="region-name">{{region.name}}</view> |
| | | <view class="region-value">{{region.value}}人</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 名片效果对比 --> |
| | | <view class="stat-card" v-if="cardComparison.length > 1"> |
| | | <view class="card-title">名片效果对比</view> |
| | | <view class="comparison-list"> |
| | | <view class="comparison-item" v-for="(card, index) in cardComparison" :key="index"> |
| | | <view class="comparison-header"> |
| | | <view class="card-name">{{card.name}}</view> |
| | | <view class="card-view"> |
| | | <text class="icon iconfont icon-eye"></text> |
| | | <text>{{card.views}}次</text> |
| | | </view> |
| | | </view> |
| | | <view class="comparison-details"> |
| | | <view class="detail"> |
| | | <text>保存率</text> |
| | | <text class="rate">{{card.saveRate}}%</text> |
| | | </view> |
| | | <view class="detail"> |
| | | <text>分享率</text> |
| | | <text class="rate">{{card.shareRate}}%</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | timeRange: 'week', // week, month, year |
| | | totalViews: 0, |
| | | totalSaves: 0, |
| | | totalShares: 0, |
| | | chartData: [], |
| | | visitSources: [], |
| | | regions: [], |
| | | cardComparison: [], |
| | | currentCardId: '' |
| | | }; |
| | | }, |
| | | onLoad(options) { |
| | | if (options.current) { |
| | | this.currentCardId = options.current; |
| | | } |
| | | this.getData(); |
| | | }, |
| | | methods: { |
| | | back() { |
| | | uni.navigateBack(); |
| | | }, |
| | | // 获取所有统计数据 |
| | | getData() { |
| | | this.getOverview(); |
| | | this.getChartData(); |
| | | this.getDetailedStats(); |
| | | }, |
| | | // 获取概览数据 |
| | | getOverview() { |
| | | let _this = this; |
| | | const params = {}; |
| | | if (_this.currentCardId) { |
| | | params.business_card_id = _this.currentCardId; |
| | | } |
| | | _this._post('plus.business/business/getOverview', params, function(res) { |
| | | _this.totalViews = res.data.views || 0; |
| | | _this.totalSaves = res.data.saves || 0; |
| | | _this.totalShares = res.data.shares || 0; |
| | | }); |
| | | }, |
| | | // 获取图表数据 |
| | | getChartData() { |
| | | let _this = this; |
| | | _this._post('plus.business/business/getChartData', { |
| | | timeRange: _this.timeRange, |
| | | business_card_id: _this.currentCardId |
| | | }, function(res) { |
| | | _this.chartData = res.data || []; |
| | | }); |
| | | }, |
| | | // 获取详细统计 |
| | | getDetailedStats() { |
| | | let _this = this; |
| | | const params = { |
| | | business_card_id: _this.currentCardId |
| | | }; |
| | | |
| | | // 获取访问来源 |
| | | _this._post('plus.business/business/getVisitSources', params, function(res) { |
| | | _this.visitSources = res.data || []; |
| | | }); |
| | | |
| | | // 获取地域分布 |
| | | _this._post('plus.business/business/getRegions', params, function(res) { |
| | | _this.regions = res.data || []; |
| | | }); |
| | | |
| | | // 获取名片对比数据 |
| | | _this._post('plus.business/business/getCardComparison', {}, function(res) { |
| | | _this.cardComparison = res.data || []; |
| | | }); |
| | | }, |
| | | // 设置时间范围 |
| | | setTimeRange(range) { |
| | | this.timeRange = range; |
| | | this.getChartData(); |
| | | }, |
| | | // 获取柱状图高度 |
| | | getBarHeight(value) { |
| | | // 简单计算柱状图高度,实际项目中可能需要更复杂的计算 |
| | | const maxValue = Math.max(...this.chartData.map(item => item.views)); |
| | | if (maxValue === 0) return '0%'; |
| | | const height = (value / maxValue) * 100; |
| | | return Math.max(height, 10) + '%'; // 最小高度为10% |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .content { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .overview-section { |
| | | display: flex; |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 30rpx 0; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .overview-item { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .number { |
| | | font-size: 48rpx; |
| | | font-weight: bold; |
| | | color: #37bde6; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .label { |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .chart-section { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .chart-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 30rpx; |
| | | |
| | | .chart-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .chart-tabs { |
| | | display: flex; |
| | | |
| | | .tab { |
| | | padding: 8rpx 20rpx; |
| | | margin-left: 10rpx; |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | border-radius: 20rpx; |
| | | |
| | | &.active { |
| | | background: #37bde6; |
| | | color: #fff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .chart-placeholder { |
| | | height: 300rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: #999; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .chart-list { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | justify-content: space-between; |
| | | height: 300rpx; |
| | | padding: 20rpx 0; |
| | | |
| | | .chart-item { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .chart-bar { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: flex-end; |
| | | width: 60rpx; |
| | | |
| | | .bar { |
| | | background: linear-gradient(to top, #37bde6, #44bbff); |
| | | width: 100%; |
| | | border-radius: 6rpx 6rpx 0 0; |
| | | display: flex; |
| | | align-items: flex-start; |
| | | justify-content: center; |
| | | padding-top: 10rpx; |
| | | |
| | | .bar-value { |
| | | font-size: 22rpx; |
| | | color: #666; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .chart-label { |
| | | font-size: 22rpx; |
| | | color: #999; |
| | | margin-top: 10rpx; |
| | | transform: rotate(-45deg); |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .detail-section { |
| | | .section-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .stat-card { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .card-title { |
| | | font-size: 28rpx; |
| | | font-weight: bold; |
| | | color: #666; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .source-list, |
| | | .region-list { |
| | | .source-item, |
| | | .region-item { |
| | | margin-bottom: 20rpx; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .source-name, |
| | | .region-name { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .source-value, |
| | | .region-value { |
| | | font-size: 26rpx; |
| | | color: #999; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | |
| | | .source-progress { |
| | | height: 12rpx; |
| | | background: #f0f0f0; |
| | | border-radius: 6rpx; |
| | | |
| | | .progress-bar { |
| | | height: 100%; |
| | | background: #37bde6; |
| | | border-radius: 6rpx; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .comparison-list { |
| | | .comparison-item { |
| | | padding: 20rpx; |
| | | background: #f5f5f5; |
| | | border-radius: 10rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .comparison-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15rpx; |
| | | |
| | | .card-name { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .card-view { |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | |
| | | .icon { |
| | | margin-right: 8rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .comparison-details { |
| | | display: flex; |
| | | |
| | | .detail { |
| | | margin-right: 40rpx; |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | |
| | | .rate { |
| | | margin-left: 10rpx; |
| | | color: #37bde6; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view> |
| | | <header-bar title="分享设置" :isBack="true" @click="back"></header-bar> |
| | | <view class="content"> |
| | | <!-- 分享内容设置 --> |
| | | <view class="setting-section"> |
| | | <view class="section-title">分享内容设置</view> |
| | | <view class="setting-card"> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">分享标题</view> |
| | | <view class="setting-control"> |
| | | <input type="text" class="input" v-model="shareConfig.title" placeholder="请输入分享标题" /> |
| | | </view> |
| | | </view> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">分享描述</view> |
| | | <view class="setting-control"> |
| | | <textarea class="textarea" v-model="shareConfig.desc" placeholder="请输入分享描述"></textarea> |
| | | </view> |
| | | </view> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">分享图片</view> |
| | | <view class="setting-control"> |
| | | <view class="image-upload"> |
| | | <image v-if="shareConfig.image" :src="shareConfig.image" mode="aspectFill" class="uploaded-image"></image> |
| | | <view v-else class="upload-placeholder" @click="uploadShareImage"> |
| | | <text class="icon iconfont icon-upload"></text> |
| | | <text>上传分享图片</text> |
| | | </view> |
| | | </view> |
| | | <view class="image-hint">建议尺寸: 300*300px,不超过2MB</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 分享样式设置 --> |
| | | <view class="setting-section"> |
| | | <view class="section-title">分享样式设置</view> |
| | | <view class="setting-card"> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">分享卡片样式</view> |
| | | <view class="setting-control"> |
| | | <view class="card-style-list"> |
| | | <view |
| | | class="card-style-item" |
| | | :class="{active: shareConfig.style === 'style1'}" |
| | | @click="shareConfig.style = 'style1'" |
| | | > |
| | | <image src="../../../../static/images/card-style1.png" mode="aspectFit"></image> |
| | | <view class="check-icon" v-if="shareConfig.style === 'style1'"></view> |
| | | </view> |
| | | <view |
| | | class="card-style-item" |
| | | :class="{active: shareConfig.style === 'style2'}" |
| | | @click="shareConfig.style = 'style2'" |
| | | > |
| | | <image src="../../../../static/images/card-style2.png" mode="aspectFit"></image> |
| | | <view class="check-icon" v-if="shareConfig.style === 'style2'"></view> |
| | | </view> |
| | | <view |
| | | class="card-style-item" |
| | | :class="{active: shareConfig.style === 'style3'}" |
| | | @click="shareConfig.style = 'style3'" |
| | | > |
| | | <image src="../../../../static/images/card-style3.png" mode="aspectFit"></image> |
| | | <view class="check-icon" v-if="shareConfig.style === 'style3'" |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">显示联系方式</view> |
| | | <view class="setting-control"> |
| | | <switch :checked="shareConfig.showContact" @change="shareConfig.showContact = !shareConfig.showContact" /> |
| | | </view> |
| | | </view> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">显示公司信息</view> |
| | | <view class="setting-control"> |
| | | <switch :checked="shareConfig.showCompany" @change="shareConfig.showCompany = !shareConfig.showCompany" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 分享权限设置 --> |
| | | <view class="setting-section"> |
| | | <view class="section-title">分享权限设置</view> |
| | | <view class="setting-card"> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">允许保存名片</view> |
| | | <view class="setting-control"> |
| | | <switch :checked="shareConfig.allowSave" @change="shareConfig.allowSave = !shareConfig.allowSave" /> |
| | | </view> |
| | | </view> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">允许二次分享</view> |
| | | <view class="setting-control"> |
| | | <switch :checked="shareConfig.allowReshare" @change="shareConfig.allowReshare = !shareConfig.allowReshare" /> |
| | | </view> |
| | | </view> |
| | | <view class="setting-item"> |
| | | <view class="setting-label">分享有效期</view> |
| | | <view class="setting-control"> |
| | | <picker mode="selector" range="['永久有效', '7天', '30天', '90天']" v-model="shareConfig.expiryIndex" @change="onExpiryChange"> |
| | | <view class="picker">{{getExpiryText()}}</view> |
| | | </picker> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 保存按钮 --> |
| | | <view class="save-btn-container"> |
| | | <button class="save-btn" @click="saveConfig">保存设置</button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | currentCardId: '', |
| | | shareConfig: { |
| | | title: '', |
| | | desc: '', |
| | | image: '', |
| | | style: 'style1', |
| | | showContact: true, |
| | | showCompany: true, |
| | | allowSave: true, |
| | | allowReshare: true, |
| | | expiryIndex: 0 |
| | | } |
| | | }; |
| | | }, |
| | | onLoad(options) { |
| | | if (options.current) { |
| | | this.currentCardId = options.current; |
| | | } |
| | | this.getShareConfig(); |
| | | }, |
| | | methods: { |
| | | back() { |
| | | uni.navigateBack(); |
| | | }, |
| | | // 获取分享配置 |
| | | getShareConfig() { |
| | | let _this = this; |
| | | _this._post('plus.business/business/getShareConfig', { |
| | | business_card_id: _this.currentCardId |
| | | }, function(res) { |
| | | if (res.data) { |
| | | _this.shareConfig = { |
| | | title: res.data.title || '', |
| | | desc: res.data.desc || '', |
| | | image: res.data.image || '', |
| | | style: res.data.style || 'style1', |
| | | showContact: res.data.show_contact === 1, |
| | | showCompany: res.data.show_company === 1, |
| | | allowSave: res.data.allow_save === 1, |
| | | allowReshare: res.data.allow_reshare === 1, |
| | | expiryIndex: res.data.expiry_index || 0 |
| | | }; |
| | | } |
| | | }); |
| | | }, |
| | | // 上传分享图片 |
| | | uploadShareImage() { |
| | | let _this = this; |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | sizeType: ['compressed'], |
| | | sourceType: ['album', 'camera'], |
| | | success: function(res) { |
| | | const tempFilePath = res.tempFilePaths[0]; |
| | | _this._uploadFile('upload/image', tempFilePath, function(res) { |
| | | if (res.data.url) { |
| | | _this.shareConfig.image = res.data.url; |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | // 获取有效期文本 |
| | | getExpiryText() { |
| | | const expiryOptions = ['永久有效', '7天', '30天', '90天']; |
| | | return expiryOptions[this.shareConfig.expiryIndex] || expiryOptions[0]; |
| | | }, |
| | | // 有效期选择变化 |
| | | onExpiryChange(e) { |
| | | this.shareConfig.expiryIndex = e.detail.value; |
| | | }, |
| | | // 保存配置 |
| | | saveConfig() { |
| | | let _this = this; |
| | | |
| | | // 简单验证 |
| | | if (!_this.shareConfig.title) { |
| | | uni.showToast({ title: '请输入分享标题', icon: 'none' }); |
| | | return; |
| | | } |
| | | |
| | | const params = { |
| | | business_card_id: _this.currentCardId, |
| | | title: _this.shareConfig.title, |
| | | desc: _this.shareConfig.desc, |
| | | image: _this.shareConfig.image, |
| | | style: _this.shareConfig.style, |
| | | show_contact: _this.shareConfig.showContact ? 1 : 0, |
| | | show_company: _this.shareConfig.showCompany ? 1 : 0, |
| | | allow_save: _this.shareConfig.allowSave ? 1 : 0, |
| | | allow_reshare: _this.shareConfig.allowReshare ? 1 : 0, |
| | | expiry_index: _this.shareConfig.expiryIndex |
| | | }; |
| | | |
| | | _this._post('plus.business/business/saveShareConfig', params, function(res) { |
| | | if (res.code === 0) { |
| | | uni.showToast({ title: '保存成功' }); |
| | | uni.navigateBack(); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .content { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .setting-section { |
| | | margin-bottom: 30rpx; |
| | | |
| | | .section-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .setting-card { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 0 30rpx; |
| | | |
| | | .setting-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 28rpx 0; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .setting-label { |
| | | flex: 1; |
| | | font-size: 30rpx; |
| | | color: #333; |
| | | } |
| | | |
| | | .setting-control { |
| | | flex: 2; |
| | | text-align: right; |
| | | |
| | | .input, |
| | | .textarea { |
| | | border: 1rpx solid #e0e0e0; |
| | | border-radius: 10rpx; |
| | | padding: 15rpx; |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | ext-align: left; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .textarea { |
| | | height: 150rpx; |
| | | resize: none; |
| | | } |
| | | |
| | | .image-upload { |
| | | display: inline-block; |
| | | |
| | | .uploaded-image { |
| | | width: 200rpx; |
| | | height: 200rpx; |
| | | border-radius: 10rpx; |
| | | } |
| | | |
| | | .upload-placeholder { |
| | | width: 200rpx; |
| | | height: 200rpx; |
| | | border: 2rpx dashed #e0e0e0; |
| | | border-radius: 10rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: #999; |
| | | font-size: 26rpx; |
| | | |
| | | .icon { |
| | | font-size: 60rpx; |
| | | margin-bottom: 10rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .image-hint { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | margin-top: 10rpx; |
| | | text-align: left; |
| | | } |
| | | |
| | | .card-style-list { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | flex-wrap: wrap; |
| | | |
| | | .card-style-item { |
| | | position: relative; |
| | | width: 220rpx; |
| | | height: 320rpx; |
| | | border: 2rpx solid #e0e0e0; |
| | | border-radius: 10rpx; |
| | | overflow: hidden; |
| | | |
| | | &.active { |
| | | border-color: #37bde6; |
| | | } |
| | | |
| | | image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .check-icon { |
| | | position: absolute; |
| | | top: 10rpx; |
| | | right: 10rpx; |
| | | width: 36rpx; |
| | | height: 36rpx; |
| | | background: #37bde6; |
| | | border-radius: 50%; |
| | | &::after { |
| | | content: ''; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | transform: translate(-50%, -50%) rotate(45deg); |
| | | width: 12rpx; |
| | | height: 20rpx; |
| | | border-right: 4rpx solid #fff; |
| | | border-bottom: 4rpx solid #fff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .picker { |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | padding: 10rpx 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .save-btn-container { |
| | | padding: 30rpx 0; |
| | | |
| | | .save-btn { |
| | | width: 100%; |
| | | height: 90rpx; |
| | | background: #37bde6; |
| | | color: #fff; |
| | | font-size: 32rpx; |
| | | border-radius: 45rpx; |
| | | line-height: 90rpx; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <view> |
| | | <header-bar title="访客记录" :isBack="true" @click="back"></header-bar> |
| | | <view class="content"> |
| | | <!-- 搜索栏 --> |
| | | <view class="search-bar"> |
| | | <view class="search-input"> |
| | | <text class="icon iconfont icon-sousuo"></text> |
| | | <input type="text" v-model="search" placeholder="搜索访客姓名" @confirm="searchVisitor" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 访客列表 --> |
| | | <scroll-view scroll-y="true" class="visitor-list" @scrolltolower="loadMore"> |
| | | <view v-if="visitors.length > 0"> |
| | | <view class="visitor-item" v-for="(visitor, index) in visitors" :key="index"> |
| | | <image class="visitor-avatar" :src="visitor.avatar || '/static/default.png'" mode="aspectFill"></image> |
| | | <view class="visitor-info"> |
| | | <view class="visitor-header"> |
| | | <view class="visitor-name">{{visitor.user_name || '未知访客'}}</view> |
| | | <view class="visitor-time">{{formatTime(visitor.visit_time)}}</view> |
| | | </view> |
| | | <view v-if="visitor.company_name" class="visitor-company">{{visitor.company_name}}</view> |
| | | <view v-if="visitor.position" class="visitor-position">{{visitor.position}}</view> |
| | | <view class="visitor-action"> |
| | | <view class="action-btn" @click="viewCard(visitor.business_card_id)"> |
| | | <text class="icon iconfont icon-card"></text> |
| | | <text>查看名片</text> |
| | | </view> |
| | | <view class="action-btn" @click="contactVisitor(visitor)"> |
| | | <text class="icon iconfont icon-message"></text> |
| | | <text>联系访客</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 无数据提示 --> |
| | | <view v-else-if="!loading" class="no-data"> |
| | | <text class="icon iconfont icon-wushuju"></text> |
| | | <text class="text">暂无访客记录</text> |
| | | </view> |
| | | |
| | | <!-- 加载中 --> |
| | | <view v-if="loading && visitors.length > 0" class="loading-more"> |
| | | <text>加载中...</text> |
| | | </view> |
| | | |
| | | <!-- 无更多数据 --> |
| | | <view v-if="!hasMore && visitors.length > 0" class="no-more"> |
| | | <text>没有更多了</text> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | visitors: [], |
| | | loading: false, |
| | | page: 1, |
| | | list_rows: 10, |
| | | hasMore: true, |
| | | search: '' |
| | | }; |
| | | }, |
| | | onLoad() { |
| | | this.getVisitors(); |
| | | }, |
| | | methods: { |
| | | back() { |
| | | uni.navigateBack(); |
| | | }, |
| | | // 获取访客列表 |
| | | getVisitors() { |
| | | let _this = this; |
| | | _this.loading = true; |
| | | const params = { |
| | | page: _this.page, |
| | | list_rows: _this.list_rows |
| | | }; |
| | | if (_this.search) { |
| | | params.search = _this.search; |
| | | } |
| | | _this._post('plus.business/business/getVisitors', params, function(res) { |
| | | _this.loading = false; |
| | | if (_this.page === 1) { |
| | | _this.visitors = res.data.list; |
| | | } else { |
| | | _this.visitors = _this.visitors.concat(res.data.list); |
| | | } |
| | | // 判断是否还有更多数据 |
| | | _this.hasMore = _this.visitors.length < res.data.total; |
| | | }); |
| | | }, |
| | | // 搜索访客 |
| | | searchVisitor() { |
| | | this.page = 1; |
| | | this.getVisitors(); |
| | | }, |
| | | // 加载更多 |
| | | loadMore() { |
| | | if (!this.loading && this.hasMore) { |
| | | this.page++; |
| | | this.getVisitors(); |
| | | } |
| | | }, |
| | | // 查看访客名片 |
| | | viewCard(business_card_id) { |
| | | this.gotoPage(`/pages/plus/business/detail?business_card_id=${business_card_id}`); |
| | | }, |
| | | // 联系访客 |
| | | contactVisitor(visitor) { |
| | | // 这里可以根据系统功能扩展,比如发送消息、拨打电话等 |
| | | uni.showActionSheet({ |
| | | itemList: ['发送消息', '拨打电话'], |
| | | success: (res) => { |
| | | if (res.tapIndex === 0) { |
| | | // 发送消息 |
| | | this.showError('消息功能暂未开放'); |
| | | } else if (res.tapIndex === 1) { |
| | | // 拨打电话 |
| | | if (visitor.phone) { |
| | | uni.makePhoneCall({ |
| | | phoneNumber: visitor.phone |
| | | }); |
| | | } else { |
| | | this.showError('暂无联系电话'); |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | // 格式化时间 |
| | | formatTime(time) { |
| | | if (!time) return ''; |
| | | const date = new Date(time); |
| | | const now = new Date(); |
| | | const diff = now - date; |
| | | const days = Math.floor(diff / (1000 * 60 * 60 * 24)); |
| | | if (days === 0) { |
| | | return '今天 ' + date.getHours() + ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); |
| | | } else if (days === 1) { |
| | | return '昨天 ' + date.getHours() + ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); |
| | | } else if (days < 7) { |
| | | return days + '天前'; |
| | | } else { |
| | | return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .content { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .search-bar { |
| | | background: #fff; |
| | | padding: 20rpx; |
| | | border-radius: 10rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .search-input { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #f5f5f5; |
| | | border-radius: 60rpx; |
| | | padding: 0 30rpx; |
| | | |
| | | .icon { |
| | | font-size: 32rpx; |
| | | color: #999; |
| | | margin-right: 20rpx; |
| | | } |
| | | |
| | | input { |
| | | flex: 1; |
| | | height: 80rpx; |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .visitor-list { |
| | | height: calc(100vh - 200rpx); |
| | | |
| | | .visitor-item { |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | display: flex; |
| | | |
| | | .visitor-avatar { |
| | | width: 120rpx; |
| | | height: 120rpx; |
| | | border-radius: 50%; |
| | | background: #f5f5f5; |
| | | } |
| | | |
| | | .visitor-info { |
| | | flex: 1; |
| | | margin-left: 30rpx; |
| | | |
| | | .visitor-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 10rpx; |
| | | |
| | | .visitor-name { |
| | | font-size: 34rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .visitor-time { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | .visitor-company, |
| | | .visitor-position { |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | margin-bottom: 5rpx; |
| | | } |
| | | |
| | | .visitor-action { |
| | | display: flex; |
| | | margin-top: 20rpx; |
| | | |
| | | .action-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-right: 40rpx; |
| | | |
| | | .icon { |
| | | font-size: 28rpx; |
| | | color: #37bde6; |
| | | margin-right: 8rpx; |
| | | } |
| | | |
| | | text { |
| | | font-size: 26rpx; |
| | | color: #37bde6; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .no-data { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 400rpx; |
| | | |
| | | .icon { |
| | | font-size: 120rpx; |
| | | color: #ccc; |
| | | margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .text { |
| | | font-size: 30rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | .loading-more, |
| | | .no-more { |
| | | text-align: center; |
| | | padding: 30rpx 0; |
| | | font-size: 28rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | import request from '@/utils/request'; |
| | | |
| | | let BusinessApi = { |
| | | |
| | | /*模板列表*/ |
| | | templateList(data, errorback) { |
| | | return request._post('/shop/plus.business.template/index', data, errorback); |
| | | }, |
| | | /*模板详情*/ |
| | | templateDetail(data, errorback) { |
| | | return request._get('/shop/plus.business.template/edit', data, errorback); |
| | | }, |
| | | /*添加模板*/ |
| | | templateAdd(data, errorback) { |
| | | return request._post('/shop/plus.business.template/add', data, errorback); |
| | | }, |
| | | /*保存模板*/ |
| | | templateSave(data, errorback) { |
| | | return request._post('/shop/plus.business.template/add', data, errorback); |
| | | }, |
| | | /*修改模板*/ |
| | | templateEdit(data, errorback) { |
| | | return request._post('/shop/plus.business.template/edit', data, errorback); |
| | | }, |
| | | /*删除模板*/ |
| | | templateDelete(data, errorback) { |
| | | return request._post('/shop/plus.business.template/delete', data, errorback); |
| | | }, |
| | | /*设置默认模板*/ |
| | | templateDefault(data, errorback) { |
| | | return request._get('/shop/plus.business.template/add', data, errorback); |
| | | }, |
| | | /*名片管理列表*/ |
| | | businessList(data, errorback) { |
| | | return request._post('/shop/plus.business.business/index', data, errorback); |
| | | }, |
| | | /*名片详情*/ |
| | | businessDetail(data, errorback) { |
| | | return request._get('/shop/plus.business.business/edit', data, errorback); |
| | | }, |
| | | /*添加名片*/ |
| | | businessAdd(data, errorback) { |
| | | return request._post('/shop/plus.business.business/add', data, errorback); |
| | | }, |
| | | /*修改名片*/ |
| | | businessEdit(data, errorback) { |
| | | return request._post('/shop/plus.business.business/edit', data, errorback); |
| | | }, |
| | | /*删除名片*/ |
| | | businessDelete(data, errorback) { |
| | | return request._post('/shop/plus.business.business/delete', data, errorback); |
| | | } |
| | | }; |
| | | |
| | | export default BusinessApi; |
| New file |
| | |
| | | <template> |
| | | |
| | | </template> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <!-- |
| | | 作者:系统 |
| | | 时间:2025-10-28 |
| | | 描述:名片等级管理 |
| | | --> |
| | | <div class="common-seach-wrap"> |
| | | <div class="search-left"> |
| | | <el-button type="primary" @click="addGrade" v-auth="'/plus/business/grade/add'">添加等级</el-button> |
| | | </div> |
| | | |
| | | <div class="table-container"> |
| | | <el-table v-loading="loading" :data="listData" border> |
| | | <el-table-column prop="grade_id" label="等级ID" width="80" align="center"></el-table-column> |
| | | <el-table-column prop="name" label="等级名称" align="center"></el-table-column> |
| | | <el-table-column prop="price" label="查看联系方式价格" align="center"> |
| | | <template slot-scope="scope"> |
| | | {{ scope.row.price>0 ? '¥' + scope.row.price : '免费' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="weight" label="权重" align="center"> |
| | | <!--<template slot-scope="scope"> |
| | | <el-input-number |
| | | v-model="scope.row.weight" |
| | | :min="0" |
| | | size="mini" |
| | | @change="handleWeightChange(scope.row)" |
| | | v-auth="'/plus/business/grade/edit'" |
| | | ></el-input-number> |
| | | </template>--> |
| | | </el-table-column> |
| | | <el-table-column prop="create_time" label="创建时间" width="180" align="center"> |
| | | </el-table-column> |
| | | <el-table-column prop="update_time" label="更新时间" width="180" align="center"> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="180" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | type="text" |
| | | @click="editGrade(scope.row)" |
| | | v-auth="'/plus/business/grade/edit'" |
| | | >编辑</el-button> |
| | | <el-button |
| | | type="text" |
| | | @click="deleteGrade(scope.row)" |
| | | v-auth="'/plus/business/grade/delete'" |
| | | >删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <!-- 添加/编辑等级对话框 --> |
| | | <el-dialog |
| | | :title="dialogTitle" |
| | | :visible.sync="dialogVisible" |
| | | width="500px" |
| | | @close="handleClose" |
| | | > |
| | | <el-form :model="formData" :rules="rules" ref="formData" label-width="150px"> |
| | | <el-form-item label="等级名称" prop="name"> |
| | | <el-input v-model="formData.name" placeholder="请输入等级名称"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="查看联系方式价格" prop="price"> |
| | | <el-input |
| | | v-model.number="formData.price" |
| | | placeholder="请输入价格,0表示免费" |
| | | type="number" |
| | | :min="0" |
| | | :step="0.01" |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="权重" prop="weight"> |
| | | <el-input-number |
| | | v-model="formData.weight" |
| | | :min="0" |
| | | placeholder="权重越小,排序越靠前" |
| | | ></el-input-number> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确定</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import PlusApi from '@/api/plus.js'; |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | listData: [], |
| | | dialogVisible: false, |
| | | dialogTitle: '', |
| | | formData: { |
| | | grade_id: '', |
| | | name: '', |
| | | price: 0.00, |
| | | weight: 100 |
| | | }, |
| | | rules: { |
| | | name: [ |
| | | { required: true, message: '请输入等级名称', trigger: 'blur' }, |
| | | { min: 1, max: 20, message: '等级名称长度在 1 到 20 个字符', trigger: 'blur' } |
| | | ], |
| | | price: [ |
| | | { type: 'number', min: 0, message: '价格不能小于0', trigger: 'blur' } |
| | | ], |
| | | weight: [ |
| | | { type: 'number', required: true, message: '请输入权重', trigger: 'blur' }, |
| | | { type: 'number', min: 0, message: '权重不能小于0', trigger: 'blur' } |
| | | ] |
| | | } |
| | | }; |
| | | }, |
| | | mounted() { |
| | | this.loadData(); |
| | | }, |
| | | methods: { |
| | | // 加载数据 |
| | | loadData() { |
| | | this.loading = true; |
| | | PlusApi.gradeIndex().then(res => { |
| | | this.loading = false; |
| | | this.listData = res.data.list || []; |
| | | console.log(this.listData); |
| | | |
| | | }).catch(() => { |
| | | this.loading = false; |
| | | this.$message.error('获取数据失败'); |
| | | }); |
| | | }, |
| | | |
| | | // 添加等级 |
| | | addGrade() { |
| | | this.dialogTitle = '添加等级'; |
| | | this.formData = { |
| | | grade_id: '', |
| | | name: '', |
| | | price: 0.00, |
| | | weight: 100 |
| | | }; |
| | | this.dialogVisible = true; |
| | | }, |
| | | |
| | | // 编辑等级 |
| | | editGrade(row) { |
| | | this.dialogTitle = '编辑等级'; |
| | | this.formData = { |
| | | grade_id: row.grade_id, |
| | | name: row.name, |
| | | price: parseFloat(row.price) || 0.00, |
| | | weight: parseInt(row.weight) || 100 |
| | | }; |
| | | this.dialogVisible = true; |
| | | }, |
| | | |
| | | // 关闭对话框 |
| | | handleClose() { |
| | | this.$refs.formData.resetFields(); |
| | | }, |
| | | |
| | | // 提交表单 |
| | | submitForm() { |
| | | this.$refs.formData.validate((valid) => { |
| | | if (valid) { |
| | | const params = { ...this.formData }; |
| | | |
| | | if (params.grade_id) { |
| | | // 编辑 |
| | | PlusApi.gradeEdit(params).then(res => { |
| | | if (res.code === 1) { |
| | | this.$message.success('编辑成功'); |
| | | this.dialogVisible = false; |
| | | this.loadData(); |
| | | } else { |
| | | this.$message.error(res.msg || '编辑失败'); |
| | | } |
| | | }).catch(() => { |
| | | this.$message.error('编辑失败'); |
| | | }); |
| | | } else { |
| | | // 添加 |
| | | PlusApi.gradeAdd(params).then(res => { |
| | | if (res.code === 1) { |
| | | this.$message.success('添加成功'); |
| | | this.dialogVisible = false; |
| | | this.loadData(); |
| | | } else { |
| | | this.$message.error(res.msg || '添加失败'); |
| | | } |
| | | }).catch(() => { |
| | | this.$message.error('添加失败'); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 删除等级 |
| | | deleteGrade(row) { |
| | | this.$confirm('确定要删除这个等级吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | PlusApi.gradeDelete({ grade_id: row.grade_id }).then(res => { |
| | | if (res.code === 1) { |
| | | this.$message.success('删除成功'); |
| | | this.loadData(); |
| | | } else { |
| | | this.$message.error(res.msg || '删除失败'); |
| | | } |
| | | }).catch(() => { |
| | | this.$message.error('删除失败'); |
| | | }); |
| | | }).catch(() => {}); |
| | | }, |
| | | |
| | | // 处理权重变化 |
| | | handleWeightChange(row) { |
| | | const params = { |
| | | grade_id: row.grade_id, |
| | | weight: row.weight, |
| | | name: row.name, |
| | | price: row.price |
| | | }; |
| | | |
| | | PlusApi.gradeEdit(params).then(res => { |
| | | if (res.code !== 1) { |
| | | this.$message.error(res.msg || '更新权重失败'); |
| | | this.loadData(); // 重新加载数据 |
| | | } |
| | | }).catch(() => { |
| | | this.$message.error('更新权重失败'); |
| | | this.loadData(); |
| | | }); |
| | | }, |
| | | |
| | | // 格式化日期 |
| | | formatDate(timestamp) { |
| | | if (!timestamp) return ''; |
| | | const date = new Date(timestamp * 1000); |
| | | const year = date.getFullYear(); |
| | | const month = (date.getMonth() + 1).toString().padStart(2, '0'); |
| | | const day = date.getDate().toString().padStart(2, '0'); |
| | | const hours = date.getHours().toString().padStart(2, '0'); |
| | | const minutes = date.getMinutes().toString().padStart(2, '0'); |
| | | const seconds = date.getSeconds().toString().padStart(2, '0'); |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | }, |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .common-seach-wrap { |
| | | padding: 20px; |
| | | background-color: #fff; |
| | | min-height: calc(100vh - 60px); |
| | | } |
| | | |
| | | .search-left { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .table-container { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <!-- |
| | | 作者:luoyiming |
| | | 时间:2019-06-04 |
| | | 描述:插件中心-分销 |
| | | --> |
| | | <div class="common-seach-wrap"> |
| | | <!--名片列表--> |
| | | <Business v-if="activeName == 'business'"></Business> |
| | | <!--名片模板记录--> |
| | | <Template v-if="activeName == 'template'"></Template> |
| | | <!--名片等级管理--> |
| | | <Grade v-if="activeName == 'grade'"></Grade> |
| | | <!--行业管理--> |
| | | <Industry v-if="activeName == 'industry'"></Industry> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import bus from '@/utils/eventBus.js'; |
| | | import PlusApi from '@/api/plus.js'; |
| | | import Business from './business/index.vue'; |
| | | import Template from './template/index.vue'; |
| | | import Grade from './grade/index.vue'; |
| | | import Industry from './industry/index.vue'; |
| | | |
| | | export default { |
| | | components: { |
| | | Business, |
| | | Template, |
| | | Grade, |
| | | Industry |
| | | }, |
| | | data() { |
| | | return { |
| | | formInline: { |
| | | nick_name: '' |
| | | }, |
| | | /*参数*/ |
| | | param: {}, |
| | | /*当前选中*/ |
| | | activeName: 'business', |
| | | /*切换数组*/ |
| | | sourceList: [ |
| | | { |
| | | key: 'industry', |
| | | value: '行业管理', |
| | | path: '/plus/business/industry/index', |
| | | |
| | | }, |
| | | { |
| | | key: 'business', |
| | | value: '名片列表', |
| | | path:'/plus/business/business/index' |
| | | }, |
| | | { |
| | | key: 'template', |
| | | value: '名片模板记录', |
| | | path:'/plus/business/template/index' |
| | | }, |
| | | { |
| | | key: 'grade', |
| | | value: '名片等级管理', |
| | | path:'/plus/business/grade/index' |
| | | } |
| | | ], |
| | | /*权限筛选后的数据*/ |
| | | tabList:[], |
| | | /*判断third是否有参数*/ |
| | | is_third_param: false |
| | | }; |
| | | }, |
| | | watch:{ |
| | | |
| | | //监听路由 |
| | | $route(to, from) { |
| | | this.init(); |
| | | } |
| | | }, |
| | | created() { |
| | | |
| | | this.init(); |
| | | |
| | | }, |
| | | beforeDestroy() { |
| | | //发送类别切换 |
| | | bus.$emit('tabData', { active: null, tab_type:'business',list: [] }); |
| | | bus.$off('activeValue'); |
| | | }, |
| | | methods: { |
| | | |
| | | /*初始化方法*/ |
| | | init(){ |
| | | this.tabList=this.authFilter(); |
| | | |
| | | if(this.tabList.length>0){ |
| | | this.activeName=this.tabList[0].key; |
| | | } |
| | | |
| | | if (this.$route.query.type != null) { |
| | | this.activeName = this.$route.query.type; |
| | | } |
| | | |
| | | /*监听传插件的值*/ |
| | | bus.$on('activeValue', res => { |
| | | if (this.is_third_param) { |
| | | this.param.user_id = ''; |
| | | this.is_third_param = false; |
| | | } |
| | | this.activeName = res; |
| | | }); |
| | | |
| | | //发送类别切换 |
| | | let params = { |
| | | active: this.activeName, |
| | | list: this.tabList, |
| | | tab_type:'business' |
| | | }; |
| | | bus.$emit('tabData', params); |
| | | }, |
| | | |
| | | /*权限过滤*/ |
| | | authFilter(){ |
| | | let list=[]; |
| | | for(let i=0;i<this.sourceList.length;i++){ |
| | | let item=this.sourceList[i]; |
| | | if(this.$filter.isAuth(item.path)){ |
| | | list.push(item); |
| | | } |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | } |
| | | }; |
| | | </script> |
| New file |
| | |
| | | <template> |
| | | <!-- |
| | | 描述:行业管理-添加 |
| | | --> |
| | | <el-dialog title="添加行业" :visible.sync="dialogVisible" @close="dialogFormVisible" :close-on-click-modal="false" |
| | | :close-on-press-escape="false"> |
| | | <el-form size="small" :model="form" :rules="formRules" ref="form"> |
| | | <el-form-item label="所属行业" :label-width="formLabelWidth"> |
| | | <el-select v-model="form.parent_id"> |
| | | <el-option label="顶级行业" value="0"></el-option> |
| | | <el-option :value="cat.industry_id" :label="cat.name" :key="cat.industry_id" v-for="cat in addform.industryOptions"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="行业名称" prop="name" :label-width="formLabelWidth"> |
| | | <el-input v-model="form.name" autocomplete="off"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="排序" prop="sort" :label-width="formLabelWidth"> |
| | | <el-input v-model.number="form.sort" autocomplete="off"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="dialogFormVisible">取 消</el-button> |
| | | <el-button type="primary" @click="addIndustry" :loading="loading">确 定</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | import PlusApi from '@/api/plus'; |
| | | export default { |
| | | data() { |
| | | return { |
| | | form: { |
| | | parent_id: '0', |
| | | name: '', |
| | | sort: 100 |
| | | }, |
| | | formRules: { |
| | | name: [{ |
| | | required: true, |
| | | message: '请输入行业名称', |
| | | trigger: 'blur' |
| | | }], |
| | | sort: [{ |
| | | required: true, |
| | | message: '排序不能为空' |
| | | }, { |
| | | type: 'number', |
| | | message: '排序必须为数字' |
| | | }] |
| | | }, |
| | | /*左边长度*/ |
| | | formLabelWidth: '120px', |
| | | /*是否显示*/ |
| | | dialogVisible: false, |
| | | loading: false |
| | | }; |
| | | }, |
| | | props: ['open_add', 'addform'], |
| | | created() { |
| | | this.dialogVisible = this.open_add; |
| | | }, |
| | | methods: { |
| | | /*添加行业*/ |
| | | addIndustry() { |
| | | let self = this; |
| | | let params = self.form; |
| | | self.$refs.form.validate((valid) => { |
| | | if (valid) { |
| | | self.loading = true; |
| | | PlusApi.addIndustry(params).then(data => { |
| | | self.loading = false; |
| | | self.$message({ |
| | | message: '添加成功', |
| | | type: 'success' |
| | | }); |
| | | self.dialogFormVisible(true); |
| | | }).catch(error => { |
| | | self.loading = false; |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | /*关闭弹窗*/ |
| | | dialogFormVisible(e) { |
| | | if (e) { |
| | | this.$emit('closeDialog', { |
| | | type: 'success', |
| | | openDialog: false |
| | | }) |
| | | } else { |
| | | this.$emit('closeDialog', { |
| | | type: 'error', |
| | | openDialog: false |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style> |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <!-- |
| | | 描述:行业管理-修改 |
| | | --> |
| | | <el-dialog title="修改行业" :visible.sync="dialogVisible" @close="dialogFormVisible" :close-on-click-modal="false" |
| | | :close-on-press-escape="false"> |
| | | <el-form size="small" :model="form" :rules="formRules" ref="form"> |
| | | <el-form-item label="所属行业" :label-width="formLabelWidth"> |
| | | <el-select v-model="form.parent_id"> |
| | | <el-option label="顶级行业" :value="0"></el-option> |
| | | <el-option :value="cat.industry_id" :label="cat.name" :key="cat.industry_id" v-for="cat in editform.industryOptions"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="行业名称" prop="name" :label-width="formLabelWidth"> |
| | | <el-input v-model="form.name" autocomplete="off"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="排序" prop="sort" :label-width="formLabelWidth"> |
| | | <el-input v-model.number="form.sort" autocomplete="off"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="dialogFormVisible">取 消</el-button> |
| | | <el-button type="primary" @click="editIndustry" :loading="loading">确 定</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | import PlusApi from '@/api/plus'; |
| | | export default { |
| | | data() { |
| | | return { |
| | | form: { |
| | | industry_id: 0, |
| | | parent_id: 0, |
| | | name: '', |
| | | sort: '' |
| | | }, |
| | | formRules: { |
| | | name: [{ |
| | | required: true, |
| | | message: '请输入行业名称', |
| | | trigger: 'blur' |
| | | }], |
| | | sort: [{ |
| | | required: true, |
| | | message: '排序不能为空' |
| | | }, { |
| | | type: 'number', |
| | | message: '排序必须为数字' |
| | | }] |
| | | }, |
| | | /*左边长度*/ |
| | | formLabelWidth: '120px', |
| | | /*是否显示*/ |
| | | dialogVisible: false, |
| | | loading: false |
| | | }; |
| | | }, |
| | | props: ['open_edit', 'editform'], |
| | | created() { |
| | | this.dialogVisible = this.open_edit; |
| | | this.form.industry_id = this.editform.model.industry_id; |
| | | this.form.parent_id = this.editform.model.parent_id; |
| | | this.form.name = this.editform.model.name; |
| | | this.form.sort = this.editform.model.sort; |
| | | }, |
| | | methods: { |
| | | /*修改行业*/ |
| | | editIndustry() { |
| | | let self = this; |
| | | let params = self.form; |
| | | self.$refs.form.validate((valid) => { |
| | | if (valid) { |
| | | self.loading = true; |
| | | PlusApi.editIndustry(params, true).then(data => { |
| | | self.loading = false; |
| | | self.$message({ |
| | | message: '修改成功', |
| | | type: 'success' |
| | | }); |
| | | self.dialogFormVisible(true); |
| | | }).catch(error => { |
| | | self.loading = false; |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | /*关闭弹窗*/ |
| | | dialogFormVisible(e) { |
| | | if (e) { |
| | | this.$emit('closeDialog', { |
| | | type: 'success', |
| | | openDialog: false |
| | | }) |
| | | } else { |
| | | this.$emit('closeDialog', { |
| | | type: 'error', |
| | | openDialog: false |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style> |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <!-- |
| | | 作者:wangxw |
| | | 时间:2023-10-26 |
| | | 描述:行业管理 |
| | | --> |
| | | <div class="product"> |
| | | |
| | | <!--添加行业--> |
| | | <div class="common-level-rail"> |
| | | <el-button size="small" type="primary" @click="addClick" icon="el-icon-plus" v-auth="'/plus/business/industry/add'">添加行业</el-button> |
| | | </div> |
| | | |
| | | <!--内容--> |
| | | <div class="product-content"> |
| | | <div class="table-wrap"> |
| | | <el-table size="small" :data="tableData" row-key="industry_id" default-expand-all :tree-props="{children: 'child'}" |
| | | style="width: 100%" v-loading="loading"> |
| | | <el-table-column prop="industry_id" label="行业ID" width="80" align="center"></el-table-column> |
| | | <el-table-column prop="name" label="行业名称" width="180"></el-table-column> |
| | | <el-table-column prop="sort" label="排序"></el-table-column> |
| | | <el-table-column prop="create_time" label="创建时间"></el-table-column> |
| | | <el-table-column prop="status" label="状态"> |
| | | <template slot-scope="scope"> |
| | | <el-checkbox v-model="scope.row.status" :checked="scope.row.status" @change="checked => statusChange(checked, scope.row)">启用</el-checkbox> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="操作" width="100"> |
| | | <template slot-scope="scope"> |
| | | <el-button @click="editClick(scope.row)" type="text" size="small" v-auth="'/plus/business/industry/edit'">编辑</el-button> |
| | | <el-button @click="deleteClick(scope.row)" type="text" size="small" v-auth="'/plus/business/industry/delete'">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | |
| | | <!--添加--> |
| | | <Add v-if="open_add" :open_add="open_add" :addform="industryModel" @closeDialog="closeDialogFunc($event, 'add')"></Add> |
| | | <!--修改--> |
| | | <Edit v-if="open_edit" :open_edit="open_edit" :editform="industryModel" @closeDialog="closeDialogFunc($event, 'edit')"></Edit> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import PlusApi from '@/api/plus'; |
| | | import Add from './Add.vue'; |
| | | import Edit from './Edit.vue'; |
| | | export default { |
| | | components: { |
| | | Add, |
| | | Edit |
| | | }, |
| | | data() { |
| | | return { |
| | | /*是否加载完成*/ |
| | | loading: true, |
| | | /*列表数据*/ |
| | | tableData: [], |
| | | /*是否打开添加弹窗*/ |
| | | open_add: false, |
| | | /*是否打开编辑弹窗*/ |
| | | open_edit: false, |
| | | /*当前编辑的对象*/ |
| | | industryModel: { |
| | | industryOptions: [], |
| | | model: {} |
| | | } |
| | | }; |
| | | }, |
| | | created() { |
| | | /*获取列表*/ |
| | | this.getData(); |
| | | }, |
| | | methods: { |
| | | /*获取列表*/ |
| | | getData() { |
| | | let self = this; |
| | | PlusApi.getIndustryList({}, true) |
| | | .then(data => { |
| | | self.loading = false; |
| | | self.tableData = data.data.list.tree; |
| | | self.industryModel.industryOptions = data.data.list.tree; |
| | | }) |
| | | .catch(error => { |
| | | self.loading = false; |
| | | }); |
| | | }, |
| | | /*打开添加*/ |
| | | addClick() { |
| | | this.open_add = true; |
| | | }, |
| | | |
| | | /*打开编辑*/ |
| | | editClick(item) { |
| | | this.industryModel.model = item; |
| | | this.open_edit = true; |
| | | }, |
| | | |
| | | /*关闭弹窗*/ |
| | | closeDialogFunc(e, f) { |
| | | if (f == 'add') { |
| | | this.open_add = e.openDialog; |
| | | if (e.type == 'success') { |
| | | this.getData(); |
| | | } |
| | | } |
| | | if (f == 'edit') { |
| | | this.open_edit = e.openDialog; |
| | | if (e.type == 'success') { |
| | | this.getData(); |
| | | } |
| | | } |
| | | }, |
| | | /*删除行业*/ |
| | | deleteClick(row) { |
| | | let self = this; |
| | | self.$confirm('删除后不可恢复,确认删除该记录吗?', '提示', { |
| | | type: 'warning' |
| | | }).then(() => { |
| | | PlusApi.deleteIndustry({ |
| | | industry_id: row.industry_id |
| | | }).then(data => { |
| | | self.$message({ |
| | | message: '删除成功', |
| | | type: 'success' |
| | | }); |
| | | self.getData(); |
| | | }); |
| | | }); |
| | | }, |
| | | /*启用/禁用*/ |
| | | statusChange: function(checked, row) { |
| | | let self = this; |
| | | let status = checked ? 1 : 0; |
| | | self.loading = true; |
| | | let params = { |
| | | industry_id: row.industry_id, |
| | | parent_id: row.parent_id, |
| | | name: row.name, |
| | | sort: row.sort, |
| | | status: status |
| | | }; |
| | | PlusApi.editIndustry( |
| | | params, |
| | | true |
| | | ) |
| | | .then(data => { |
| | | self.loading = false; |
| | | row.status = checked; |
| | | }) |
| | | .catch(error => { |
| | | self.loading = false; |
| | | row.status = checked ? 0 : 1; |
| | | }); |
| | | }, |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .product { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .common-level-rail { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .product-content { |
| | | clear: both; |
| | | } |
| | | |
| | | .table-wrap { |
| | | background: #fff; |
| | | padding: 20px; |
| | | border-radius: 4px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <!-- |
| | | 作者:系统自动生成 |
| | | 时间:当前日期 |
| | | 描述:插件中心-名片模板-添加模板 |
| | | --> |
| | | <div class="user" v-loading="loading"> |
| | | <div class="common-form">名片模板添加</div> |
| | | <div class="poster-box d-s-s"> |
| | | <div class="left-box"> |
| | | <div v-if="form.backdrop" class="img"><img v-img-url="form.backdrop.src" /></div> |
| | | <div class="userinfo"> |
| | | <!-- 头像 --> |
| | | <div v-if="form.avatar.display == 1" |
| | | class="photo pa" |
| | | v-drag="{type:'avatar',obj:this}" |
| | | :class="{ radius: form.avatar.style == 'circle' }" |
| | | :style="'width:' + form.avatar.width + 'px;height:' + form.avatar.width + 'px;top:' + form.avatar.top + 'px;left:' + form.avatar.left + 'px;background-color:#f0f0f0;display:flex;align-items:center;justify-content:center;color:#999'"> |
| | | <span>头像</span> |
| | | </div> |
| | | |
| | | <!-- Logo --> |
| | | <div v-if="form.logo.display == 1" |
| | | class="logo pa" |
| | | v-drag="{type:'logo',obj:this}" |
| | | :class="{ radius: form.logo.style == 'circle' }" |
| | | :style="'width:' + form.logo.width + 'px;height:' + form.logo.height + 'px;top:' + form.logo.top + 'px;left:' + form.logo.left + 'px;'"> |
| | | <img v-img-url="form.logo.src" alt="" /> |
| | | </div> |
| | | |
| | | <!-- 姓名 --> |
| | | <div class="name pa" |
| | | v-drag="{type:'name',obj:this}" |
| | | :style="'font-size:' + form.name.fontSize + 'px;color:' + form.name.color + ';top:' + form.name.top + 'px;left:' + form.name.left + 'px;'"> |
| | | 这里是姓名 |
| | | </div> |
| | | |
| | | <!-- 手机 --> |
| | | <div class="mobile pa" |
| | | v-drag="{type:'mobile',obj:this}" |
| | | :style="'font-size:' + form.mobile.fontSize + 'px;color:' + form.mobile.color + ';top:' + form.mobile.top + 'px;left:' + form.mobile.left + 'px;'"> |
| | | 手机:134xxxxxxxx |
| | | </div> |
| | | |
| | | <!-- 公司 --> |
| | | <div v-for="(item, index) in form.unit" :key="'unit' + index" |
| | | :class="'unit' + index + ' pa'" |
| | | v-drag="{type:'unit', index:index, obj:this}" |
| | | :style="'font-size:' + form.unit[index].fontSize + 'px;color:' + form.unit[index].color + ';top:' + form.unit[index].top + 'px;left:' + form.unit[index].left + 'px;'"> |
| | | 这是公司{{index+1}} |
| | | </div> |
| | | |
| | | <!-- 职位 --> |
| | | <div v-for="(item, index) in form.duties" :key="'duties' + index" |
| | | :class="'duties' + index + ' pa'" |
| | | v-drag="{type:'duties', index:index, obj:this}" |
| | | :style="'font-size:' + form.duties[index].fontSize + 'px;color:' + form.duties[index].color + ';top:' + form.duties[index].top + 'px;left:' + form.duties[index].left + 'px;'"> |
| | | 这是职位{{index+1}} |
| | | </div> |
| | | |
| | | <!-- 地址 --> |
| | | <div v-for="(item, index) in form.address" :key="'address' + index" |
| | | :class="'address' + index + ' pa'" |
| | | v-drag="{type:'address', index:index, obj:this}" |
| | | :style="'font-size:' + form.address[index].fontSize + 'px;color:' + form.address[index].color + ';top:' + form.address[index].top + 'px;left:' + form.address[index].left + 'px;'"> |
| | | 地址:广西壮族自治区南宁市江南区壮锦大道八桂绿城·龙湖御景-A栋-2单元{{index+1}}号 |
| | | </div> |
| | | |
| | | <!-- 微信 --> |
| | | <div class="wechat pa" |
| | | v-drag="{type:'wechat',obj:this}" |
| | | :style="'font-size:' + form.wechat.fontSize + 'px;color:' + form.wechat.color + ';top:' + form.wechat.top + 'px;left:' + form.wechat.left + 'px;'"> |
| | | 微信:134xxxxxxxx |
| | | </div> |
| | | |
| | | <!-- 邮箱 --> |
| | | <div class="mailbox pa" |
| | | v-drag="{type:'mailbox',obj:this}" |
| | | :style="'font-size:' + form.mailbox.fontSize + 'px;color:' + form.mailbox.color + ';top:' + form.mailbox.top + 'px;left:' + form.mailbox.left + 'px;'"> |
| | | 邮箱:134xxxxxxxx@.xxx.com |
| | | </div> |
| | | |
| | | <!-- 电话 --> |
| | | <div class="phone pa" |
| | | v-drag="{type:'phone',obj:this}" |
| | | :style="'font-size:' + form.phone.fontSize + 'px;color:' + form.phone.color + ';top:' + form.phone.top + 'px;left:' + form.phone.left + 'px;'"> |
| | | 电话:xxx-xxx-xxx |
| | | </div> |
| | | |
| | | <!-- 自定义图标 --> |
| | | <div v-for="(item, index) in form.iconL" :key="'iconL' + index" |
| | | :class="'iconL' + index + ' pa icon'" |
| | | v-drag="{type:'iconL', index:index, obj:this}" |
| | | :style="'width:' + form.iconL[index].width + 'px;height:' + form.iconL[index].height + 'px;top:' + form.iconL[index].top + 'px;left:' + form.iconL[index].left + 'px;'"> |
| | | <img v-img-url="form.iconL[index].src" alt="" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="right-box flex-1"> |
| | | <el-form size="small" ref="form" :model="form" label-width="150px"> |
| | | <!-- 背景图 --> |
| | | <el-form-item label="海报背景图"> |
| | | <el-button type="primary" @click="openUpload(1)">上传图片</el-button> |
| | | <div class="tips">尺寸建议:宽750像素 高大于(等于)1200像素</div> |
| | | </el-form-item> |
| | | |
| | | <!-- 头像设置 --> |
| | | <el-form-item label="是否显示头像"> |
| | | <el-radio v-model="form.avatar.display" label="1">显示</el-radio> |
| | | <el-radio v-model="form.avatar.display" label="0">隐藏</el-radio> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.avatar.display == 1" label="头像宽度" prop="avatar.width" :rules="[{ required: true, message: '请输入头像宽度' }]"> |
| | | <el-input v-model.number="form.avatar.width" min="30" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.avatar.display == 1" label="头像样式"> |
| | | <el-radio v-model="form.avatar.style" label="square">正方形</el-radio> |
| | | <el-radio v-model="form.avatar.style" label="circle">圆形</el-radio> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.avatar.display == 1" label="头像位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.avatar.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.avatar.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- Logo设置 --> |
| | | <el-form-item label="是否显示Logo"> |
| | | <el-radio v-model="form.logo.display" label="1">显示</el-radio> |
| | | <el-radio v-model="form.logo.display" label="0">隐藏</el-radio> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.logo.display == 1" label="Logo图片"> |
| | | <el-button type="primary" @click="openUpload(2)">上传图片</el-button> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.logo.display == 1" label="Logo尺寸" prop="logo.width" :rules="[{ required: true, message: '请输入Logo宽度' }]"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.logo.width" min="10" type="number" class="max-w200" placeholder="宽度"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.logo.height" min="10" type="number" class="max-w200" placeholder="高度"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.logo.display == 1" label="Logo样式"> |
| | | <el-radio v-model="form.logo.style" label="square">正方形</el-radio> |
| | | <el-radio v-model="form.logo.style" label="circle">圆形</el-radio> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.logo.display == 1" label="Logo位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.logo.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.logo.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 姓名设置 --> |
| | | <el-form-item label="姓名字体大小" prop="name.fontSize" :rules="[{ required: true, message: '请输入字体大小' }]"> |
| | | <el-input v-model.number="form.name.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="姓名字体颜色"> |
| | | <el-color-picker v-model="form.name.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="姓名位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.name.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.name.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 手机设置 --> |
| | | <el-form-item label="手机字体大小" prop="mobile.fontSize" :rules="[{ required: true, message: '请输入字体大小' }]"> |
| | | <el-input v-model.number="form.mobile.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="手机字体颜色"> |
| | | <el-color-picker v-model="form.mobile.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="手机位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.mobile.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.mobile.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 图标数量设置 --> |
| | | <el-form-item label="图标数量"> |
| | | <div class="d-s-r"> |
| | | <el-button type="text" @click="editIcon" :disabled="form.iconL.length <= 0"> |
| | | <i class="el-icon-minus"></i> |
| | | </el-button> |
| | | <span class="mr-10 ml-10">{{ form.iconL.length }}</span> |
| | | <el-button type="text" @click="addIcon"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 图标设置 --> |
| | | <div v-for="(item, index) in form.iconL" :key="'icon_setting' + index" class="icon-setting"> |
| | | <el-form-item :label="'图标' + (index + 1)"> |
| | | <el-button type="primary" @click="openUpload(3, index)">上传图片</el-button> |
| | | </el-form-item> |
| | | <el-form-item :label="'图标' + (index + 1) + '尺寸'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.iconL[index].width" min="10" type="number" class="max-w200" placeholder="宽度"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.iconL[index].height" min="10" type="number" class="max-w200" placeholder="高度"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item :label="'图标' + (index + 1) + '位置'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.iconL[index].left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.iconL[index].top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 公司数量设置 --> |
| | | <el-form-item label="公司数量"> |
| | | <div class="d-s-r"> |
| | | <el-button type="text" @click="editUnit" :disabled="form.unit.length <= 0"> |
| | | <i class="el-icon-minus"></i> |
| | | </el-button> |
| | | <span class="mr-10 ml-10">{{ form.unit.length }}</span> |
| | | <el-button type="text" @click="addUnit"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 公司设置 --> |
| | | <div v-for="(item, index) in form.unit" :key="'unit_setting' + index" class="unit-setting"> |
| | | <el-form-item :label="'公司' + (index + 1) + '字体大小'"> |
| | | <el-input v-model.number="form.unit[index].fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item :label="'公司' + (index + 1) + '字体颜色'"> |
| | | <el-color-picker v-model="form.unit[index].color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item :label="'公司' + (index + 1) + '位置'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.unit[index].left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.unit[index].top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 职位数量设置 --> |
| | | <el-form-item label="职位数量"> |
| | | <div class="d-s-r"> |
| | | <el-button type="text" @click="editDuties" :disabled="form.duties.length <= 0"> |
| | | <i class="el-icon-minus"></i> |
| | | </el-button> |
| | | <span class="mr-10 ml-10">{{ form.duties.length }}</span> |
| | | <el-button type="text" @click="addDuties"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 职位设置 --> |
| | | <div v-for="(item, index) in form.duties" :key="'duties_setting' + index" class="duties-setting"> |
| | | <el-form-item :label="'职位' + (index + 1) + '字体大小'"> |
| | | <el-input v-model.number="form.duties[index].fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item :label="'职位' + (index + 1) + '字体颜色'"> |
| | | <el-color-picker v-model="form.duties[index].color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item :label="'职位' + (index + 1) + '位置'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.duties[index].left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.duties[index].top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 地址数量设置 --> |
| | | <el-form-item label="地址数量"> |
| | | <div class="d-s-r"> |
| | | <el-button type="text" @click="editAddress" :disabled="form.address.length <= 0"> |
| | | <i class="el-icon-minus"></i> |
| | | </el-button> |
| | | <span class="mr-10 ml-10">{{ form.address.length }}</span> |
| | | <el-button type="text" @click="addAddress"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 地址设置 --> |
| | | <div v-for="(item, index) in form.address" :key="'address_setting' + index" class="address-setting"> |
| | | <el-form-item :label="'地址' + (index + 1) + '字体大小'"> |
| | | <el-input v-model.number="form.address[index].fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item :label="'地址' + (index + 1) + '字体颜色'"> |
| | | <el-color-picker v-model="form.address[index].color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item :label="'地址' + (index + 1) + '位置'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.address[index].left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.address[index].top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 微信设置 --> |
| | | <el-form-item label="微信字体大小"> |
| | | <el-input v-model.number="form.wechat.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="微信字体颜色"> |
| | | <el-color-picker v-model="form.wechat.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="微信位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.wechat.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.wechat.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 邮箱设置 --> |
| | | <el-form-item label="邮箱字体大小"> |
| | | <el-input v-model.number="form.mailbox.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="邮箱字体颜色"> |
| | | <el-color-picker v-model="form.mailbox.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="邮箱位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.mailbox.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.mailbox.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 电话设置 --> |
| | | <el-form-item label="电话字体大小"> |
| | | <el-input v-model.number="form.phone.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="电话字体颜色"> |
| | | <el-color-picker v-model="form.phone.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="电话位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.phone.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.phone.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 提交按钮 --> |
| | | <div class="common-button-wrapper"> |
| | | <el-button @click="back">返回</el-button> |
| | | <el-button type="primary" @click="onSubmit" :loading="loading">提交</el-button> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 上传图片组件 --> |
| | | <Upload v-if="isupload" :isupload="isupload" :type="uploadType" :index="uploadIndex" @returnImgs="returnImgsFunc">上传图片</Upload> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import Upload from '@/components/file/Upload'; |
| | | import BusinessApi from '@/api/business'; |
| | | |
| | | export default { |
| | | components: { |
| | | Upload |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | isupload: false, |
| | | uploadType: 1, |
| | | uploadIndex: 0, |
| | | form: { |
| | | backdrop: { |
| | | src: '', |
| | | height: 0, |
| | | width: 0, |
| | | type: 'backdrop' |
| | | }, |
| | | is_business: 0, |
| | | name: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 232, |
| | | top: 13, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }, |
| | | avatar: { |
| | | width: 70, |
| | | style: 'circle', |
| | | left: 380, |
| | | top: 37, |
| | | display: '1', |
| | | src: '', |
| | | type: 'avatar' |
| | | }, |
| | | logo: { |
| | | width: 70, |
| | | height: 27, |
| | | style: 'square', |
| | | left: 22, |
| | | top: 24, |
| | | display: '1', |
| | | src: '', |
| | | type: 'image' |
| | | }, |
| | | mobile: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 192, |
| | | top: 43, |
| | | fontWeight: 400 |
| | | }, |
| | | address: [ |
| | | { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 133, |
| | | top: 206, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | } |
| | | ], |
| | | unit: [ |
| | | { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 133, |
| | | top: 167, |
| | | fontWeight: 100, |
| | | type: 'text' |
| | | } |
| | | ], |
| | | duties: [ |
| | | { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 260, |
| | | top: 167, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | } |
| | | ], |
| | | position: [], |
| | | wechat: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 205, |
| | | top: 65, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }, |
| | | mailbox: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 205, |
| | | top: 104, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }, |
| | | phone: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 205, |
| | | top: 84, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }, |
| | | positionNum: 0, |
| | | iconL: [] |
| | | } |
| | | }; |
| | | }, |
| | | created() { |
| | | this.loadData(); |
| | | |
| | | // 在组件创建后初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | // 通过遍历this.form对象动态调用dragEventArray方法 |
| | | Object.keys(this.form).forEach(key => { |
| | | this.dragEventArray(key); |
| | | }); |
| | | }); |
| | | }, |
| | | directives: { |
| | | drag: { |
| | | inserted: function(el, binding) { |
| | | // 为元素添加drag和对应的type类名,方便dragEventArray方法选择 |
| | | const type = binding.value.type; |
| | | el.classList.add('drag'); |
| | | el.classList.add(type); |
| | | |
| | | // 避免指令和dragEventArray方法重复绑定拖拽事件 |
| | | // 实际的拖拽逻辑由dragEventArray方法处理 |
| | | el.style.position = 'absolute'; |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | // 拖拽事件初始化方法,用于处理数组类型元素的拖拽 |
| | | dragEventArray(type) { |
| | | // 使用类选择器获取对应的元素,更准确高效 |
| | | const elements = document.querySelectorAll(`.${type}`); |
| | | |
| | | elements.forEach((el, index) => { |
| | | // 检查是否已经绑定过拖拽事件,避免重复绑定 |
| | | if (el.getAttribute('drag-handler') === 'true') return; |
| | | el.setAttribute('drag-handler', 'true'); |
| | | |
| | | // 为每个元素添加拖拽事件 |
| | | el.onmousedown = (event) => { |
| | | event.preventDefault(); |
| | | |
| | | // 计算鼠标按下位置与元素左上角的偏移 |
| | | let sentX = event.clientX - el.offsetLeft; |
| | | let sentY = event.clientY - el.offsetTop; |
| | | |
| | | // 获取父容器的边界 |
| | | let parent = el.parentElement; |
| | | let l = 0; |
| | | let t = 0; |
| | | let r = parent.offsetWidth - el.offsetWidth; |
| | | let b = parent.offsetHeight - el.offsetHeight; |
| | | |
| | | document.onmousemove = (event) => { |
| | | event.preventDefault(); |
| | | |
| | | // 计算新位置 |
| | | let slideLeft = event.clientX - sentX; |
| | | let slideTop = event.clientY - sentY; |
| | | |
| | | // 限制在父容器内 |
| | | slideLeft <= l && (slideLeft = l); |
| | | slideLeft >= r && (slideLeft = r); |
| | | slideTop <= t && (slideTop = t); |
| | | slideTop >= b && (slideTop = b); |
| | | |
| | | // 更新位置 |
| | | if (Array.isArray(this.form[type]) && this.form[type][index]) { |
| | | // 使用Vue.set确保响应式更新,这样右侧表单的位置输入框也能同步更新 |
| | | this.$set(this.form[type][index], 'left', slideLeft); |
| | | this.$set(this.form[type][index], 'top', slideTop); |
| | | |
| | | // 直接更新DOM样式,确保视觉效果即时生效 |
| | | el.style.left = slideLeft + 'px'; |
| | | el.style.top = slideTop + 'px'; |
| | | |
| | | // 强制Vue更新,确保表单输入框同步更新 |
| | | this.$forceUpdate(); |
| | | } else { // 使用Vue.set确保响应式更新,这样右侧表单的位置输入框也能同步更新 |
| | | this.$set(this.form[type], 'left', slideLeft); |
| | | this.$set(this.form[type], 'top', slideTop); |
| | | // 直接更新DOM样式,确保视觉效果即时生效 |
| | | el.style.left = slideLeft + 'px'; |
| | | el.style.top = slideTop + 'px'; |
| | | // 强制Vue更新,确保表单输入框同步更新 |
| | | this.$forceUpdate(); |
| | | } |
| | | }; |
| | | |
| | | document.onmouseup = () => { |
| | | document.onmousemove = null; |
| | | document.onmouseup = null; |
| | | }; |
| | | |
| | | return false; |
| | | }; |
| | | }); |
| | | }, |
| | | |
| | | // 加载数据 |
| | | loadData() { |
| | | let self = this; |
| | | self.loading = true; |
| | | BusinessApi.templateDefault({}, true) |
| | | .then(res => { |
| | | if (res.data.data) { |
| | | try { |
| | | const data = JSON.parse(res.data.data); |
| | | self.form = data; |
| | | } catch (e) { |
| | | console.error('解析数据失败', e); |
| | | } |
| | | } |
| | | self.loading = false; |
| | | }) |
| | | .catch(error => { |
| | | self.loading = false; |
| | | console.error('加载数据失败', error); |
| | | }); |
| | | }, |
| | | |
| | | // 拖动处理 |
| | | dragDiv(x, y, type, index) { |
| | | // 确保坐标值有效 |
| | | x = Math.max(0, x || 0); |
| | | y = Math.max(0, y || 0); |
| | | |
| | | // 确保form对象存在 |
| | | if (!this.form) { |
| | | console.warn('Form object not initialized'); |
| | | return; |
| | | } |
| | | |
| | | switch (type) { |
| | | case 'avatar': |
| | | if (this.form.avatar) { |
| | | this.form.avatar.left = x; |
| | | this.form.avatar.top = y; |
| | | } |
| | | break; |
| | | case 'logo': |
| | | if (this.form.logo) { |
| | | this.form.logo.left = x; |
| | | this.form.logo.top = y; |
| | | } |
| | | break; |
| | | case 'name': |
| | | if (this.form.name) { |
| | | this.form.name.left = x; |
| | | this.form.name.top = y; |
| | | } |
| | | break; |
| | | case 'mobile': |
| | | if (this.form.mobile) { |
| | | this.form.mobile.left = x; |
| | | this.form.mobile.top = y; |
| | | } |
| | | break; |
| | | case 'wechat': |
| | | if (this.form.wechat) { |
| | | this.form.wechat.left = x; |
| | | this.form.wechat.top = y; |
| | | } |
| | | break; |
| | | case 'mailbox': |
| | | if (this.form.mailbox) { |
| | | this.form.mailbox.left = x; |
| | | this.form.mailbox.top = y; |
| | | } |
| | | break; |
| | | case 'phone': |
| | | if (this.form.phone) { |
| | | this.form.phone.left = x; |
| | | this.form.phone.top = y; |
| | | } |
| | | break; |
| | | case 'unit': |
| | | // 增强公司元素的拖拽处理 |
| | | if (Array.isArray(this.form.unit) && this.form.unit[index]) { |
| | | // 确保对象存在并具有left和top属性 |
| | | if (!this.form.unit[index].left && this.form.unit[index].left !== 0) { |
| | | this.form.unit[index].left = 0; |
| | | } |
| | | if (!this.form.unit[index].top && this.form.unit[index].top !== 0) { |
| | | this.form.unit[index].top = 0; |
| | | } |
| | | this.form.unit[index].left = x; |
| | | this.form.unit[index].top = y; |
| | | } else { |
| | | console.warn('Unit element not found at index:', index); |
| | | } |
| | | break; |
| | | case 'duties': |
| | | // 增强职位元素的拖拽处理 |
| | | if (Array.isArray(this.form.duties) && this.form.duties[index]) { |
| | | if (!this.form.duties[index].left && this.form.duties[index].left !== 0) { |
| | | this.form.duties[index].left = 0; |
| | | } |
| | | if (!this.form.duties[index].top && this.form.duties[index].top !== 0) { |
| | | this.form.duties[index].top = 0; |
| | | } |
| | | this.form.duties[index].left = x; |
| | | this.form.duties[index].top = y; |
| | | } else { |
| | | console.warn('Duties element not found at index:', index); |
| | | } |
| | | break; |
| | | case 'address': |
| | | // 增强地址元素的拖拽处理 |
| | | if (Array.isArray(this.form.address) && this.form.address[index]) { |
| | | if (!this.form.address[index].left && this.form.address[index].left !== 0) { |
| | | this.form.address[index].left = 0; |
| | | } |
| | | if (!this.form.address[index].top && this.form.address[index].top !== 0) { |
| | | this.form.address[index].top = 0; |
| | | } |
| | | this.form.address[index].left = x; |
| | | this.form.address[index].top = y; |
| | | } else { |
| | | console.warn('Address element not found at index:', index); |
| | | } |
| | | break; |
| | | case 'iconL': |
| | | if (Array.isArray(this.form.iconL) && this.form.iconL[index]) { |
| | | this.form.iconL[index].left = x; |
| | | this.form.iconL[index].top = y; |
| | | } |
| | | break; |
| | | default: |
| | | console.warn('Unknown drag type:', type); |
| | | } |
| | | }, |
| | | |
| | | // 打开上传 |
| | | openUpload(type, index) { |
| | | this.uploadType = type; |
| | | this.uploadIndex = index; |
| | | this.isupload = true; |
| | | }, |
| | | |
| | | // 返回图片 |
| | | returnImgsFunc(e) { |
| | | if (e && e.length > 0) { |
| | | switch (this.uploadType) { |
| | | case 1: // 背景图 |
| | | this.form.backdrop.src = e[0].file_path; |
| | | // 这里可以添加获取图片尺寸的逻辑 |
| | | break; |
| | | case 2: // Logo |
| | | this.form.logo.src = e[0].file_path; |
| | | break; |
| | | case 3: // 图标 |
| | | if (!this.form.iconL[this.uploadIndex]) { |
| | | this.form.iconL[this.uploadIndex] = {}; |
| | | } |
| | | this.form.iconL[this.uploadIndex].src = e[0].file_path; |
| | | break; |
| | | } |
| | | } |
| | | this.isupload = false; |
| | | }, |
| | | |
| | | // 添加图标 |
| | | addIcon() { |
| | | this.form.iconL.push({ |
| | | src: '', |
| | | width: 30, |
| | | height: 30, |
| | | left: 100, |
| | | top: 100 |
| | | });// 添加后重新初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | this.dragEventArray('iconL'); |
| | | }); |
| | | }, |
| | | |
| | | // 减少图标 |
| | | editIcon() { |
| | | if (this.form.iconL.length > 0) { |
| | | this.form.iconL.pop(); |
| | | } |
| | | }, |
| | | |
| | | // 添加公司 |
| | | addUnit() { |
| | | const index = this.form.unit.length; |
| | | this.form.unit.push({ |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 133, |
| | | top: 167 + (index * 20), |
| | | fontWeight: 100, |
| | | type: 'text' |
| | | }); |
| | | |
| | | // 添加后重新初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | this.dragEventArray('unit'); |
| | | }); |
| | | }, |
| | | |
| | | // 减少公司 |
| | | editUnit() { |
| | | if (this.form.unit.length > 0) { |
| | | this.form.unit.pop(); |
| | | } |
| | | }, |
| | | |
| | | // 添加职位 |
| | | addDuties() { |
| | | const index = this.form.duties.length; |
| | | this.form.duties.push({ |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 260, |
| | | top: 167 + (index * 20), |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }); |
| | | |
| | | // 添加后重新初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | this.dragEventArray('duties'); |
| | | }); |
| | | }, |
| | | |
| | | // 减少职位 |
| | | editDuties() { |
| | | if (this.form.duties.length > 0) { |
| | | this.form.duties.pop(); |
| | | } |
| | | }, |
| | | |
| | | // 添加地址 |
| | | addAddress() { |
| | | const index = this.form.address.length; |
| | | this.form.address.push({ |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 133, |
| | | top: 206 + (index * 20), |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }); |
| | | |
| | | // 添加后重新初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | this.dragEventArray('address'); |
| | | }); |
| | | }, |
| | | |
| | | // 减少地址 |
| | | editAddress() { |
| | | if (this.form.address.length > 0) { |
| | | this.form.address.pop(); |
| | | } |
| | | }, |
| | | |
| | | // 提交表单 |
| | | onSubmit() { |
| | | let self = this; |
| | | self.$refs.form.validate(valid => { |
| | | if (valid) { |
| | | self.loading = true; |
| | | |
| | | // 确保必要字段存在 |
| | | if (!self.form.backdrop.src) { |
| | | self.$message.error('请上传背景图'); |
| | | self.loading = false; |
| | | return; |
| | | } |
| | | |
| | | BusinessApi.templateSave({ |
| | | template: self.form |
| | | }, true) |
| | | .then(res => { |
| | | self.loading = false; |
| | | self.$message({ |
| | | message: '保存成功', |
| | | type: 'success' |
| | | }); |
| | | self.$router.push('/plus/business/template/index'); |
| | | }) |
| | | .catch(error => { |
| | | self.loading = false; |
| | | self.$message.error('保存失败,请重试'); |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 返回 |
| | | back() { |
| | | this.$router.push('/plus/business/template/index'); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .poster-box { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .poster-box .left-box { |
| | | position: relative; |
| | | overflow: hidden; |
| | | margin: 0 30px; |
| | | border: 1px solid #e0e0e0; |
| | | border-radius: 8px; |
| | | background-color: #f5f5f5; |
| | | } |
| | | |
| | | .poster-box .left-box .img img { |
| | | width: auto; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .poster-box .left-box .userinfo { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .poster-box .left-box .pa { |
| | | position: absolute; |
| | | cursor: move; |
| | | } |
| | | |
| | | .poster-box .left-box .photo, |
| | | .poster-box .left-box .logo, |
| | | .poster-box .left-box .icon { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | overflow: hidden; |
| | | background: #ffffff; |
| | | border: 1px solid #e0e0e0; |
| | | } |
| | | |
| | | .poster-box .left-box .photo.radius, |
| | | .poster-box .left-box .logo.radius, |
| | | .poster-box .left-box .icon.radius { |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .poster-box .left-box .photo img, |
| | | .poster-box .left-box .logo img, |
| | | .poster-box .left-box .icon img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: contain; |
| | | } |
| | | |
| | | .poster-box .left-box .name, |
| | | .poster-box .left-box .mobile, |
| | | .poster-box .left-box .unit, |
| | | .poster-box .left-box .duties, |
| | | .poster-box .left-box .address, |
| | | .poster-box .left-box .wechat, |
| | | .poster-box .left-box .mailbox, |
| | | .poster-box .left-box .phone { |
| | | padding: 2px 5px; |
| | | white-space: nowrap; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .poster-box .right-box { |
| | | flex: 1; |
| | | min-width: 400px; |
| | | max-height: 800px; |
| | | overflow-y: auto; |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | .tips { |
| | | margin-top: 5px; |
| | | color: #999; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .d-s-r { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .max-w460 { |
| | | max-width: 460px; |
| | | } |
| | | |
| | | .max-w200 { |
| | | max-width: 200px; |
| | | } |
| | | |
| | | .mr-10 { |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .ml-10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .icon-setting { |
| | | border: 1px solid #e0e0e0; |
| | | padding: 10px; |
| | | margin-bottom: 10px; |
| | | border-radius: 4px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <!-- |
| | | 作者:系统自动生成 |
| | | 时间:当前日期 |
| | | 描述:插件中心-名片模板-编辑模板 |
| | | --> |
| | | <div class="user" v-loading="loading"> |
| | | <div class="common-form">名片模板编辑</div> |
| | | <div class="poster-box d-s-s"> |
| | | <div class="left-box"> |
| | | <div v-if="form.backdrop" class="img"><img v-img-url="form.backdrop.src" /></div> |
| | | <div class="userinfo"> |
| | | <!-- 头像 --> |
| | | <div v-if="form.avatar.display == 1" |
| | | class="photo pa" |
| | | v-drag="{type:'avatar',obj:this}" |
| | | :class="{ radius: form.avatar.style == 'circle' }" |
| | | :style="'width:' + form.avatar.width + 'px;height:' + form.avatar.width + 'px;top:' + form.avatar.top + 'px;left:' + form.avatar.left + 'px;background-color:#f0f0f0;display:flex;align-items:center;justify-content:center;color:#999'"> |
| | | <span>头像</span> |
| | | </div> |
| | | |
| | | <!-- Logo --> |
| | | <div v-if="form.logo.display == 1" |
| | | class="logo pa" |
| | | v-drag="{type:'logo',obj:this}" |
| | | :class="{ radius: form.logo.style == 'circle' }" |
| | | :style="'width:' + form.logo.width + 'px;height:' + form.logo.height + 'px;top:' + form.logo.top + 'px;left:' + form.logo.left + 'px;'"> |
| | | <img v-img-url="form.logo.src" alt="" /> |
| | | </div> |
| | | |
| | | <!-- 姓名 --> |
| | | <div class="name pa" |
| | | v-drag="{type:'name',obj:this}" |
| | | :style="'font-size:' + form.name.fontSize + 'px;color:' + form.name.color + ';top:' + form.name.top + 'px;left:' + form.name.left + 'px;'"> |
| | | 这里是姓名 |
| | | </div> |
| | | |
| | | <!-- 手机 --> |
| | | <div class="mobile pa" |
| | | v-drag="{type:'mobile',obj:this}" |
| | | :style="'font-size:' + form.mobile.fontSize + 'px;color:' + form.mobile.color + ';top:' + form.mobile.top + 'px;left:' + form.mobile.left + 'px;'"> |
| | | 手机:134xxxxxxxx |
| | | </div> |
| | | |
| | | <!-- 公司 --> |
| | | <div v-for="(item, index) in form.unit" :key="'unit' + index" |
| | | :class="'unit' + index + ' pa unit'" |
| | | v-drag="{type:'unit', index:index, obj:this}" |
| | | :style="'font-size:' + form.unit[index].fontSize + 'px;color:' + form.unit[index].color + ';top:' + form.unit[index].top + 'px;left:' + form.unit[index].left + 'px;'"> |
| | | 这是公司{{index+1}} |
| | | </div> |
| | | |
| | | <!-- 职位 --> |
| | | <div v-for="(item, index) in form.duties" :key="'duties' + index" |
| | | :class="'duties' + index + ' pa duties'" |
| | | v-drag="{type:'duties', index:index, obj:this}" |
| | | :style="'font-size:' + form.duties[index].fontSize + 'px;color:' + form.duties[index].color + ';top:' + form.duties[index].top + 'px;left:' + form.duties[index].left + 'px;'"> |
| | | 这是职位{{index+1}} |
| | | </div> |
| | | |
| | | <!-- 地址 --> |
| | | <div v-for="(item, index) in form.address" :key="'address' + index" |
| | | :class="'address' + index + ' pa address'" |
| | | v-drag="{type:'address', index:index, obj:this}" |
| | | :style="'font-size:' + form.address[index].fontSize + 'px;color:' + form.address[index].color + ';top:' + form.address[index].top + 'px;left:' + form.address[index].left + 'px;'"> |
| | | 地址:广西壮族自治区南宁市江南区壮锦大道八桂绿城·龙湖御景-A栋-2单元{{index+1}}号 |
| | | </div> |
| | | |
| | | <!-- 微信 --> |
| | | <div class="wechat pa" |
| | | v-drag="{type:'wechat',obj:this}" |
| | | :style="'font-size:' + form.wechat.fontSize + 'px;color:' + form.wechat.color + ';top:' + form.wechat.top + 'px;left:' + form.wechat.left + 'px;'"> |
| | | 微信:134xxxxxxxx |
| | | </div> |
| | | |
| | | <!-- 邮箱 --> |
| | | <div class="mailbox pa" |
| | | v-drag="{type:'mailbox',obj:this}" |
| | | :style="'font-size:' + form.mailbox.fontSize + 'px;color:' + form.mailbox.color + ';top:' + form.mailbox.top + 'px;left:' + form.mailbox.left + 'px;'"> |
| | | 邮箱:134xxxxxxxx@.xxx.com |
| | | </div> |
| | | |
| | | <!-- 电话 --> |
| | | <div class="phone pa" |
| | | v-drag="{type:'phone',obj:this}" |
| | | :style="'font-size:' + form.phone.fontSize + 'px;color:' + form.phone.color + ';top:' + form.phone.top + 'px;left:' + form.phone.left + 'px;'"> |
| | | 电话:xxx-xxx-xxx |
| | | </div> |
| | | |
| | | <!-- 自定义图标 --> |
| | | <div v-for="(item, index) in form.iconL" :key="'iconL' + index" |
| | | :class="'iconL' + index + ' pa icon'" |
| | | v-drag="{type:'iconL', index:index, obj:this}" |
| | | :style="'width:' + form.iconL[index].width + 'px;height:' + form.iconL[index].height + 'px;top:' + form.iconL[index].top + 'px;left:' + form.iconL[index].left + 'px;'"> |
| | | <img v-img-url="form.iconL[index].src" alt="" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="right-box flex-1"> |
| | | <el-form size="small" ref="form" :model="form" label-width="150px"> |
| | | <!-- 背景图 --> |
| | | <el-form-item label="海报背景图"> |
| | | <el-button type="primary" @click="openUpload(1)">上传图片</el-button> |
| | | <div class="tips">尺寸建议:宽750像素 高大于(等于)1200像素</div> |
| | | </el-form-item> |
| | | |
| | | <!-- 头像设置 --> |
| | | <el-form-item label="是否显示头像"> |
| | | <el-radio v-model="form.avatar.display" label="1">显示</el-radio> |
| | | <el-radio v-model="form.avatar.display" label="0">隐藏</el-radio> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.avatar.display == 1" label="头像宽度" prop="avatar.width" :rules="[{ required: true, message: '请输入头像宽度' }]"> |
| | | <el-input v-model.number="form.avatar.width" min="30" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.avatar.display == 1" label="头像样式"> |
| | | <el-radio v-model="form.avatar.style" label="square">正方形</el-radio> |
| | | <el-radio v-model="form.avatar.style" label="circle">圆形</el-radio> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.avatar.display == 1" label="头像位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.avatar.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.avatar.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- Logo设置 --> |
| | | <el-form-item label="是否显示Logo"> |
| | | <el-radio v-model="form.logo.display" label="1">显示</el-radio> |
| | | <el-radio v-model="form.logo.display" label="0">隐藏</el-radio> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.logo.display == 1" label="Logo图片"> |
| | | <el-button type="primary" @click="openUpload(2)">上传图片</el-button> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.logo.display == 1" label="Logo尺寸" prop="logo.width" :rules="[{ required: true, message: '请输入Logo宽度' }]"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.logo.width" min="10" type="number" class="max-w200" placeholder="宽度"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.logo.height" min="10" type="number" class="max-w200" placeholder="高度"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.logo.display == 1" label="Logo样式"> |
| | | <el-radio v-model="form.logo.style" label="square">正方形</el-radio> |
| | | <el-radio v-model="form.logo.style" label="circle">圆形</el-radio> |
| | | </el-form-item> |
| | | <el-form-item v-if="form.logo.display == 1" label="Logo位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.logo.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.logo.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 姓名设置 --> |
| | | <el-form-item label="姓名字体大小" prop="name.fontSize" :rules="[{ required: true, message: '请输入字体大小' }]"> |
| | | <el-input v-model.number="form.name.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="姓名字体颜色"> |
| | | <el-color-picker v-model="form.name.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="姓名位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.name.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.name.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 手机设置 --> |
| | | <el-form-item label="手机字体大小" prop="mobile.fontSize" :rules="[{ required: true, message: '请输入字体大小' }]"> |
| | | <el-input v-model.number="form.mobile.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="手机字体颜色"> |
| | | <el-color-picker v-model="form.mobile.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="手机位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.mobile.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.mobile.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 图标数量设置 --> |
| | | <el-form-item label="图标数量"> |
| | | <div class="d-s-r"> |
| | | <el-button type="text" @click="editIcon" :disabled="form.iconL.length <= 0"> |
| | | <i class="el-icon-minus"></i> |
| | | </el-button> |
| | | <span class="mr-10 ml-10">{{ form.iconL.length }}</span> |
| | | <el-button type="text" @click="addIcon"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 图标设置 --> |
| | | <div v-for="(item, index) in form.iconL" :key="'icon_setting' + index" class="icon-setting"> |
| | | <el-form-item :label="'图标' + (index + 1)"> |
| | | <el-button type="primary" @click="openUpload(3, index)">上传图片</el-button> |
| | | </el-form-item> |
| | | <el-form-item :label="'图标' + (index + 1) + '尺寸'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.iconL[index].width" min="10" type="number" class="max-w200" placeholder="宽度"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.iconL[index].height" min="10" type="number" class="max-w200" placeholder="高度"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item :label="'图标' + (index + 1) + '位置'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.iconL[index].left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.iconL[index].top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 公司数量设置 --> |
| | | <el-form-item label="公司数量"> |
| | | <div class="d-s-r"> |
| | | <el-button type="text" @click="editUnit" :disabled="form.unit.length <= 0"> |
| | | <i class="el-icon-minus"></i> |
| | | </el-button> |
| | | <span class="mr-10 ml-10">{{ form.unit.length }}</span> |
| | | <el-button type="text" @click="addUnit"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 公司设置 --> |
| | | <div v-for="(item, index) in form.unit" :key="'unit_setting' + index" class="unit-setting"> |
| | | <el-form-item :label="'公司' + (index + 1) + '字体大小'"> |
| | | <el-input v-model.number="form.unit[index].fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item :label="'公司' + (index + 1) + '字体颜色'"> |
| | | <el-color-picker v-model="form.unit[index].color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item :label="'公司' + (index + 1) + '位置'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.unit[index].left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.unit[index].top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 职位数量设置 --> |
| | | <el-form-item label="职位数量"> |
| | | <div class="d-s-r"> |
| | | <el-button type="text" @click="editDuties" :disabled="form.duties.length <= 0"> |
| | | <i class="el-icon-minus"></i> |
| | | </el-button> |
| | | <span class="mr-10 ml-10">{{ form.duties.length }}</span> |
| | | <el-button type="text" @click="addDuties"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 职位设置 --> |
| | | <div v-for="(item, index) in form.duties" :key="'duties_setting' + index" class="duties-setting"> |
| | | <el-form-item :label="'职位' + (index + 1) + '字体大小'"> |
| | | <el-input v-model.number="form.duties[index].fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item :label="'职位' + (index + 1) + '字体颜色'"> |
| | | <el-color-picker v-model="form.duties[index].color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item :label="'职位' + (index + 1) + '位置'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.duties[index].left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.duties[index].top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 地址数量设置 --> |
| | | <el-form-item label="地址数量"> |
| | | <div class="d-s-r"> |
| | | <el-button type="text" @click="editAddress" :disabled="form.address.length <= 0"> |
| | | <i class="el-icon-minus"></i> |
| | | </el-button> |
| | | <span class="mr-10 ml-10">{{ form.address.length }}</span> |
| | | <el-button type="text" @click="addAddress"> |
| | | <i class="el-icon-plus"></i> |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 地址设置 --> |
| | | <div v-for="(item, index) in form.address" :key="'address_setting' + index" class="address-setting"> |
| | | <el-form-item :label="'地址' + (index + 1) + '字体大小'"> |
| | | <el-input v-model.number="form.address[index].fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item :label="'地址' + (index + 1) + '字体颜色'"> |
| | | <el-color-picker v-model="form.address[index].color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item :label="'地址' + (index + 1) + '位置'"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.address[index].left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.address[index].top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <!-- 微信设置 --> |
| | | <el-form-item label="微信字体大小"> |
| | | <el-input v-model.number="form.wechat.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="微信字体颜色"> |
| | | <el-color-picker v-model="form.wechat.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="微信位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.wechat.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.wechat.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 邮箱设置 --> |
| | | <el-form-item label="邮箱字体大小"> |
| | | <el-input v-model.number="form.mailbox.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="邮箱字体颜色"> |
| | | <el-color-picker v-model="form.mailbox.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="邮箱位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.mailbox.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.mailbox.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 电话设置 --> |
| | | <el-form-item label="电话字体大小"> |
| | | <el-input v-model.number="form.phone.fontSize" min="12" type="number" class="max-w460"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="电话字体颜色"> |
| | | <el-color-picker v-model="form.phone.color"></el-color-picker> |
| | | </el-form-item> |
| | | <el-form-item label="电话位置"> |
| | | <div class="d-s-r"> |
| | | <el-input v-model.number="form.phone.left" min="0" type="number" class="max-w200" placeholder="左右位置"></el-input> |
| | | <span class="mr-10">x</span> |
| | | <el-input v-model.number="form.phone.top" min="0" type="number" class="max-w200" placeholder="上下位置"></el-input> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <!-- 提交按钮 --> |
| | | <div class="common-button-wrapper"> |
| | | <el-button @click="back">返回</el-button> |
| | | <el-button type="primary" @click="onSubmit" :loading="loading">提交</el-button> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 上传图片组件 --> |
| | | <Upload v-if="isupload" :isupload="isupload" :type="uploadType" :index="uploadIndex" @returnImgs="returnImgsFunc">上传图片</Upload> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import Upload from '@/components/file/Upload'; |
| | | import BusinessApi from '@/api/business'; |
| | | |
| | | export default { |
| | | components: { |
| | | Upload |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | isupload: false, |
| | | uploadType: 1, |
| | | uploadIndex: 0, |
| | | form: { |
| | | backdrop: { |
| | | src: '', |
| | | height: 0, |
| | | width: 0, |
| | | type: 'backdrop' |
| | | }, |
| | | is_business: 0, |
| | | name: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 232, |
| | | top: 13, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }, |
| | | avatar: { |
| | | width: 70, |
| | | style: 'circle', |
| | | left: 37, |
| | | top: 37, |
| | | display: 1, |
| | | src: '', |
| | | type: 'avatar' |
| | | }, |
| | | logo: { |
| | | width: 70, |
| | | height: 27, |
| | | style: 'square', |
| | | left: 22, |
| | | src: '', |
| | | top: 24, |
| | | display: 1, |
| | | type: 'image' |
| | | }, |
| | | mobile: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 192, |
| | | top: 43, |
| | | fontWeight: 400 |
| | | }, |
| | | address: [ |
| | | { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 133, |
| | | top: 206, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | } |
| | | ], |
| | | unit: [ |
| | | { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 133, |
| | | top: 167, |
| | | fontWeight: 100, |
| | | type: 'text' |
| | | } |
| | | ], |
| | | duties: [ |
| | | { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 260, |
| | | top: 167, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | } |
| | | ], |
| | | position: [], |
| | | wechat: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 205, |
| | | top: 65, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }, |
| | | mailbox: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 205, |
| | | top: 104, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }, |
| | | phone: { |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 205, |
| | | top: 84, |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }, |
| | | positionNum: 0, |
| | | iconL: [] |
| | | } |
| | | }; |
| | | }, |
| | | directives: { |
| | | drag: { |
| | | inserted(el, binding) { |
| | | const { type, obj } = binding.value; |
| | | |
| | | // 添加类型相关的类名,用于拖拽事件的绑定 |
| | | el.classList.add('drag'); |
| | | el.classList.add(type); |
| | | |
| | | // 避免指令和dragEventArray方法重复绑定拖拽事件 |
| | | // 实际的拖拽逻辑由dragEventArray方法处理 |
| | | el.style.position = 'absolute'; |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | // 获取URL参数中的template_id |
| | | this.template_id = this.$route.query.template_id; |
| | | if (this.template_id) { |
| | | this.loadData(); |
| | | } |
| | | }, |
| | | mounted() { |
| | | // 在组件挂载完成后,为所有可拖拽元素初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | // 为数组类型的元素初始化拖拽事件 |
| | | Object.keys(this.form).forEach(key => { |
| | | this.dragEventArray(key); |
| | | }); |
| | | }); |
| | | }, |
| | | methods: { |
| | | // 拖拽事件初始化方法,用于处理数组类型元素的拖拽 |
| | | dragEventArray(type) { |
| | | // 使用类选择器获取对应的元素,更准确高效 |
| | | const elements = document.querySelectorAll(`.${type}`); |
| | | |
| | | elements.forEach((el, index) => { |
| | | // 检查是否已经绑定过拖拽事件,避免重复绑定 |
| | | if (el.getAttribute('drag-handler') === 'true') return; |
| | | el.setAttribute('drag-handler', 'true'); |
| | | |
| | | // 为每个元素添加拖拽事件 |
| | | el.onmousedown = (event) => { |
| | | event.preventDefault(); |
| | | |
| | | // 计算鼠标按下位置与元素左上角的偏移 |
| | | let sentX = event.clientX - el.offsetLeft; |
| | | let sentY = event.clientY - el.offsetTop; |
| | | |
| | | // 获取父容器的边界 |
| | | let parent = el.parentElement; |
| | | let l = 0; |
| | | let t = 0; |
| | | let r = parent.offsetWidth - el.offsetWidth; |
| | | let b = parent.offsetHeight - el.offsetHeight; |
| | | |
| | | document.onmousemove = (event) => { |
| | | event.preventDefault(); |
| | | |
| | | // 计算新位置 |
| | | let slideLeft = event.clientX - sentX; |
| | | let slideTop = event.clientY - sentY; |
| | | |
| | | // 限制在父容器内 |
| | | slideLeft <= l && (slideLeft = l); |
| | | slideLeft >= r && (slideLeft = r); |
| | | slideTop <= t && (slideTop = t); |
| | | slideTop >= b && (slideTop = b); |
| | | |
| | | // 更新位置 |
| | | if (Array.isArray(this.form[type]) && this.form[type][index]) { |
| | | // 使用Vue.set确保响应式更新,这样右侧表单的位置输入框也能同步更新 |
| | | this.$set(this.form[type][index], 'left', slideLeft); |
| | | this.$set(this.form[type][index], 'top', slideTop); |
| | | |
| | | // 直接更新DOM样式,确保视觉效果即时生效 |
| | | el.style.left = slideLeft + 'px'; |
| | | el.style.top = slideTop + 'px'; |
| | | |
| | | // 强制Vue更新,确保表单输入框同步更新 |
| | | this.$forceUpdate(); |
| | | } else { |
| | | // 使用Vue.set确保响应式更新,这样右侧表单的位置输入框也能同步更新 |
| | | this.$set(this.form[type], 'left', slideLeft); |
| | | this.$set(this.form[type], 'top', slideTop); |
| | | // 直接更新DOM样式,确保视觉效果即时生效 |
| | | el.style.left = slideLeft + 'px'; |
| | | el.style.top = slideTop + 'px'; |
| | | // 强制Vue更新,确保表单输入框同步更新 |
| | | this.$forceUpdate(); |
| | | } |
| | | }; |
| | | |
| | | document.onmouseup = () => { |
| | | document.onmousemove = null; |
| | | document.onmouseup = null; |
| | | }; |
| | | |
| | | return false; |
| | | }; |
| | | }); |
| | | }, |
| | | |
| | | // 加载数据 |
| | | loadData() { |
| | | let self = this; |
| | | self.loading = true; |
| | | BusinessApi.templateDetail({template_id: self.template_id}, true) |
| | | .then(res => { |
| | | if (res.data.data) { |
| | | try { |
| | | const data = JSON.parse(res.data.data); |
| | | // 确保所有必要的数组字段存在 |
| | | if (!data.unit) data.unit = []; |
| | | if (!data.duties) data.duties = []; |
| | | if (!data.address) data.address = []; |
| | | if (!data.iconL) data.iconL = []; |
| | | self.form = data; |
| | | |
| | | // 数据加载完成后,重新初始化拖拽事件 |
| | | self.$nextTick(() => { |
| | | ['unit', 'duties', 'address', 'iconL'].forEach(type => { |
| | | self.dragEventArray(type); |
| | | }); |
| | | }); |
| | | } catch (e) { |
| | | console.error('解析数据失败', e); |
| | | } |
| | | } |
| | | self.loading = false; |
| | | }) |
| | | .catch(error => { |
| | | self.loading = false; |
| | | console.error('加载数据失败', error); |
| | | }); |
| | | }, |
| | | |
| | | // 拖动处理 |
| | | dragDiv(x, y, type, index) { |
| | | // 确保坐标值有效 |
| | | x = Math.max(0, x || 0); |
| | | y = Math.max(0, y || 0); |
| | | |
| | | // 确保form对象存在 |
| | | if (!this.form) { |
| | | console.warn('Form object not initialized'); |
| | | return; |
| | | } |
| | | |
| | | switch (type) { |
| | | case 'avatar': |
| | | if (this.form.avatar) { |
| | | this.form.avatar.left = x; |
| | | this.form.avatar.top = y; |
| | | } |
| | | break; |
| | | case 'logo': |
| | | if (this.form.logo) { |
| | | this.form.logo.left = x; |
| | | this.form.logo.top = y; |
| | | } |
| | | break; |
| | | case 'name': |
| | | if (this.form.name) { |
| | | this.form.name.left = x; |
| | | this.form.name.top = y; |
| | | } |
| | | break; |
| | | case 'mobile': |
| | | if (this.form.mobile) { |
| | | this.form.mobile.left = x; |
| | | this.form.mobile.top = y; |
| | | } |
| | | break; |
| | | case 'wechat': |
| | | if (this.form.wechat) { |
| | | this.form.wechat.left = x; |
| | | this.form.wechat.top = y; |
| | | } |
| | | break; |
| | | case 'mailbox': |
| | | if (this.form.mailbox) { |
| | | this.form.mailbox.left = x; |
| | | this.form.mailbox.top = y; |
| | | } |
| | | break; |
| | | case 'phone': |
| | | if (this.form.phone) { |
| | | this.form.phone.left = x; |
| | | this.form.phone.top = y; |
| | | } |
| | | break; |
| | | case 'unit': |
| | | // 增强公司元素的拖拽处理 |
| | | if (Array.isArray(this.form.unit) && this.form.unit[index]) { |
| | | // 确保对象存在并具有left和top属性 |
| | | if (!this.form.unit[index].left && this.form.unit[index].left !== 0) { |
| | | this.form.unit[index].left = 0; |
| | | } |
| | | if (!this.form.unit[index].top && this.form.unit[index].top !== 0) { |
| | | this.form.unit[index].top = 0; |
| | | } |
| | | this.form.unit[index].left = x; |
| | | this.form.unit[index].top = y; |
| | | } else { |
| | | console.warn('Unit element not found at index:', index); |
| | | } |
| | | break; |
| | | case 'duties': |
| | | // 增强职位元素的拖拽处理 |
| | | if (Array.isArray(this.form.duties) && this.form.duties[index]) { |
| | | if (!this.form.duties[index].left && this.form.duties[index].left !== 0) { |
| | | this.form.duties[index].left = 0; |
| | | } |
| | | if (!this.form.duties[index].top && this.form.duties[index].top !== 0) { |
| | | this.form.duties[index].top = 0; |
| | | } |
| | | this.form.duties[index].left = x; |
| | | this.form.duties[index].top = y; |
| | | } else { |
| | | console.warn('Duties element not found at index:', index); |
| | | } |
| | | break; |
| | | case 'address': |
| | | // 增强地址元素的拖拽处理 |
| | | if (Array.isArray(this.form.address) && this.form.address[index]) { |
| | | if (!this.form.address[index].left && this.form.address[index].left !== 0) { |
| | | this.form.address[index].left = 0; |
| | | } |
| | | if (!this.form.address[index].top && this.form.address[index].top !== 0) { |
| | | this.form.address[index].top = 0; |
| | | } |
| | | this.form.address[index].left = x; |
| | | this.form.address[index].top = y; |
| | | } else { |
| | | console.warn('Address element not found at index:', index); |
| | | } |
| | | break; |
| | | case 'iconL': |
| | | if (Array.isArray(this.form.iconL) && this.form.iconL[index]) { |
| | | this.form.iconL[index].left = x; |
| | | this.form.iconL[index].top = y; |
| | | } |
| | | break; |
| | | default: |
| | | console.warn('Unknown drag type:', type); |
| | | } |
| | | }, |
| | | |
| | | // 打开上传 |
| | | openUpload(type, index) { |
| | | this.uploadType = type; |
| | | this.uploadIndex = index; |
| | | this.isupload = true; |
| | | }, |
| | | |
| | | // 返回图片 |
| | | returnImgsFunc(e) { |
| | | if (e && e.length > 0) { |
| | | switch (this.uploadType) { |
| | | case 1: // 背景图 |
| | | this.form.backdrop.src = e[0].file_path; |
| | | // 这里可以添加获取图片尺寸的逻辑 |
| | | break; |
| | | case 2: // Logo |
| | | this.form.logo.src = e[0].file_path; |
| | | break; |
| | | case 3: // 图标 |
| | | if (!this.form.iconL[this.uploadIndex]) { |
| | | this.form.iconL[this.uploadIndex] = {}; |
| | | } |
| | | this.form.iconL[this.uploadIndex].src = e[0].file_path; |
| | | break; |
| | | } |
| | | } |
| | | this.isupload = false; |
| | | }, |
| | | |
| | | // 添加图标 |
| | | addIcon() { |
| | | this.form.iconL.push({ |
| | | src: '', |
| | | width: 30, |
| | | height: 30, |
| | | left: 100, |
| | | top: 100 |
| | | }); |
| | | // 添加后重新初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | this.dragEventArray('iconL'); |
| | | }); |
| | | }, |
| | | |
| | | // 减少图标 |
| | | editIcon() { |
| | | if (this.form.iconL.length > 0) { |
| | | this.form.iconL.pop(); |
| | | } |
| | | }, |
| | | |
| | | // 添加公司 |
| | | addUnit() { |
| | | const index = this.form.unit.length; |
| | | this.form.unit.push({ |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 133, |
| | | top: 167 + (index * 20), |
| | | fontWeight: 100, |
| | | type: 'text' |
| | | }); |
| | | |
| | | // 添加后重新初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | this.dragEventArray('unit'); |
| | | }); |
| | | }, |
| | | |
| | | // 减少公司 |
| | | editUnit() { |
| | | if (this.form.unit.length > 0) { |
| | | this.form.unit.pop(); |
| | | } |
| | | }, |
| | | |
| | | // 添加职位 |
| | | addDuties() { |
| | | const index = this.form.duties.length; |
| | | this.form.duties.push({ |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 260, |
| | | top: 167 + (index * 20), |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }); |
| | | |
| | | // 添加后重新初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | this.dragEventArray('duties'); |
| | | }); |
| | | }, |
| | | |
| | | // 减少职位 |
| | | editDuties() { |
| | | if (this.form.duties.length > 0) { |
| | | this.form.duties.pop(); |
| | | } |
| | | }, |
| | | |
| | | // 添加地址 |
| | | addAddress() { |
| | | const index = this.form.address.length; |
| | | this.form.address.push({ |
| | | fontSize: 14, |
| | | color: '#000000', |
| | | left: 133, |
| | | top: 206 + (index * 20), |
| | | fontWeight: 400, |
| | | type: 'text' |
| | | }); |
| | | |
| | | // 添加后重新初始化拖拽事件 |
| | | this.$nextTick(() => { |
| | | this.dragEventArray('address'); |
| | | }); |
| | | }, |
| | | |
| | | // 减少地址 |
| | | editAddress() { |
| | | if (this.form.address.length > 0) { |
| | | this.form.address.pop(); |
| | | } |
| | | }, |
| | | |
| | | // 提交表单 |
| | | onSubmit() { |
| | | let self = this; |
| | | self.$refs.form.validate(valid => { |
| | | if (valid) { |
| | | self.loading = true; |
| | | |
| | | // 确保必要字段存在 |
| | | if (!self.form.backdrop.src) { |
| | | self.$message.error('请上传背景图'); |
| | | self.loading = false; |
| | | return; |
| | | } |
| | | |
| | | BusinessApi.templateEdit({ |
| | | template_id: self.template_id, |
| | | template: self.form |
| | | }, true) |
| | | .then(res => { |
| | | self.loading = false; |
| | | self.$message({ |
| | | message: '保存成功', |
| | | type: 'success' |
| | | }); |
| | | self.$router.push('/plus/business/template/index'); |
| | | }) |
| | | .catch(error => { |
| | | self.loading = false; |
| | | self.$message.error('保存失败,请重试'); |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 返回 |
| | | back() { |
| | | this.$router.push('/plus/business/template/index'); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .poster-box { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .poster-box .left-box { |
| | | position: relative; |
| | | overflow: hidden; |
| | | margin: 0 30px; |
| | | border: 1px solid #e0e0e0; |
| | | border-radius: 8px; |
| | | background-color: #f5f5f5; |
| | | } |
| | | |
| | | .poster-box .left-box .img img { |
| | | width: auto; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .poster-box .left-box .userinfo { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .poster-box .left-box .pa { |
| | | position: absolute; |
| | | cursor: move; |
| | | } |
| | | |
| | | .poster-box .left-box .photo, |
| | | .poster-box .left-box .logo, |
| | | .poster-box .left-box .icon { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | overflow: hidden; |
| | | background: #ffffff; |
| | | border: 1px solid #e0e0e0; |
| | | } |
| | | |
| | | .poster-box .left-box .photo.radius, |
| | | .poster-box .left-box .logo.radius, |
| | | .poster-box .left-box .icon.radius { |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .poster-box .left-box .photo img, |
| | | .poster-box .left-box .logo img, |
| | | .poster-box .left-box .icon img { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: contain; |
| | | } |
| | | |
| | | .poster-box .left-box .name, |
| | | .poster-box .left-box .mobile, |
| | | .poster-box .left-box .unit, |
| | | .poster-box .left-box .duties, |
| | | .poster-box .left-box .address, |
| | | .poster-box .left-box .wechat, |
| | | .poster-box .left-box .mailbox, |
| | | .poster-box .left-box .phone { |
| | | padding: 2px 5px; |
| | | white-space: nowrap; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .poster-box .right-box { |
| | | flex: 1; |
| | | min-width: 400px; |
| | | max-height: 800px; |
| | | overflow-y: auto; |
| | | padding: 0 20px; |
| | | } |
| | | |
| | | .tips { |
| | | margin-top: 5px; |
| | | color: #999; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .d-s-r { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .max-w460 { |
| | | max-width: 460px; |
| | | } |
| | | |
| | | .max-w200 { |
| | | max-width: 200px; |
| | | } |
| | | |
| | | .mr-10 { |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .ml-10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .icon-setting { |
| | | border: 1px solid #e0e0e0; |
| | | padding: 10px; |
| | | margin-bottom: 10px; |
| | | border-radius: 4px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <!-- |
| | | 作者:系统自动生成 |
| | | 时间:当前日期 |
| | | 描述:插件中心-名片模板-管理列表 |
| | | --> |
| | | <div class="user" v-loading="listLoading"> |
| | | <div class="common-form">名片模板管理</div> |
| | | <div class="common-main"> |
| | | <div class="common-toolbar"> |
| | | <el-button type="primary" @click="addTemplate">添加模板</el-button> |
| | | <el-button type="danger" @click="batchDelete" :disabled="multipleSelection.length === 0">批量删除</el-button> |
| | | </div> |
| | | |
| | | <el-table :data="listData" border class="common-table" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" align="center"></el-table-column> |
| | | <el-table-column prop="template_id" label="ID" width="80" align="center"></el-table-column> |
| | | <el-table-column prop="image" label="模板图片" align="center"> |
| | | <template slot-scope="scope"> |
| | | <a :href="scope.row.image" target="_blank"> |
| | | <img :src="scope.row.image" class="logo-img" alt="模板图片"> |
| | | </a> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="create_time" label="创建时间" width="180" align="center"></el-table-column> |
| | | <el-table-column label="操作" align="center" fixed="right"> |
| | | <template slot-scope="scope"> |
| | | <el-button type="primary" size="small" @click="editTemplate(scope.row)" class="m-r-5">编辑</el-button> |
| | | <el-button type="danger" size="small" @click="deleteTemplate(scope.row)" class="m-r-5">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div class="common-pagination"> |
| | | <div class="page-text">共 {{ total }} 条记录</div> |
| | | <el-pagination |
| | | layout="prev, pager, next, jumper" |
| | | :total="total" |
| | | :page-size="listQuery.pagesize" |
| | | :current-page="listQuery.page" |
| | | @current-change="pageChange" |
| | | @size-change="pageSizeChange" |
| | | ></el-pagination> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import BusinessApi from '@/api/business'; |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | total: 0, |
| | | listLoading: false, |
| | | multipleSelection: [], |
| | | listQuery: { |
| | | page: 1, |
| | | pagesize: 10, |
| | | title: '' |
| | | }, |
| | | listData: [] |
| | | }; |
| | | }, |
| | | created() { |
| | | this.getList(); |
| | | }, |
| | | methods: { |
| | | // 获取列表数据 |
| | | getList() { |
| | | const that = this; |
| | | that.listLoading = true; |
| | | BusinessApi.templateList(that.listQuery) |
| | | .then(res => { |
| | | that.listLoading = false; |
| | | that.listData = res.data.list.data; |
| | | that.total = res.data.list.total; |
| | | }) |
| | | .catch(error => { |
| | | that.listLoading = false; |
| | | console.error('获取列表失败', error); |
| | | that.$message.error('获取数据失败,请重试'); |
| | | }); |
| | | }, |
| | | // 查询 |
| | | handleQuery() { |
| | | this.listQuery.page = 1; |
| | | this.getList(); |
| | | }, |
| | | // 重置 |
| | | resetQuery() { |
| | | this.listQuery = { |
| | | page: 1, |
| | | pagesize: 10, |
| | | title: '' |
| | | }; |
| | | this.getList(); |
| | | }, |
| | | // 分页改变 |
| | | pageChange(page) { |
| | | this.listQuery.page = page; |
| | | this.getList(); |
| | | }, |
| | | // 每页条数改变 |
| | | pageSizeChange(pagesize) { |
| | | this.listQuery.pagesize = pagesize; |
| | | this.getList(); |
| | | }, |
| | | // 选择项变化 |
| | | handleSelectionChange(selection) { |
| | | this.multipleSelection = selection; |
| | | }, |
| | | // 批量删除 |
| | | batchDelete() { |
| | | if (this.multipleSelection.length === 0) { |
| | | this.$message.warning('请选择要删除的模板'); |
| | | return; |
| | | } |
| | | |
| | | const template_ids = this.multipleSelection.map(item => item.template_id).join(','); |
| | | |
| | | this.$confirm('确定要删除选中的 ' + this.multipleSelection.length + ' 个模板吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | this.listLoading = true; |
| | | BusinessApi.templateDelete({template_id: template_ids}) |
| | | .then(res => { |
| | | this.listLoading = false; |
| | | this.$message({ |
| | | message: '删除成功', |
| | | type: 'success' |
| | | }); |
| | | this.getList(); |
| | | this.multipleSelection = []; |
| | | }) |
| | | .catch(error => { |
| | | this.listLoading = false; |
| | | this.$message.error('删除失败,请重试'); |
| | | }); |
| | | }); |
| | | }, |
| | | // 添加模板 |
| | | addTemplate() { |
| | | this.$router.push('/plus/business/template/add'); |
| | | }, |
| | | // 编辑模板 |
| | | editTemplate(row) { |
| | | this.$router.push(`/plus/business/template/edit?template_id=${row.template_id}`); |
| | | }, |
| | | // 删除模板 |
| | | deleteTemplate(row) { |
| | | this.$confirm('确定要删除该模板吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | this.listLoading = true; |
| | | BusinessApi.templateDelete({template_id: row.template_id}) |
| | | .then(res => { |
| | | this.listLoading = false; |
| | | this.$message({ |
| | | message: '删除成功', |
| | | type: 'success' |
| | | }); |
| | | this.getList(); |
| | | }) |
| | | .catch(error => { |
| | | this.listLoading = false; |
| | | this.$message.error('删除失败,请重试'); |
| | | }); |
| | | }); |
| | | }, |
| | | // 更改默认状态 |
| | | changeStatus(row) { |
| | | BusinessApi.templateDefault({template_id: row.template_id, is_default: row.is_default}) |
| | | .then(res => { |
| | | this.$message({ |
| | | message: '设置成功', |
| | | type: 'success' |
| | | }); |
| | | }) |
| | | .catch(error => { |
| | | this.$message.error('设置失败,请重试'); |
| | | this.getList(); // 刷新列表以恢复正确状态 |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .logo-img { |
| | | height: 170px; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .common-search { |
| | | margin-bottom: 15px; |
| | | padding: 10px 15px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .common-toolbar { |
| | | margin-bottom: 15px; |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .common-table { |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .common-pagination { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .page-text { |
| | | color: #606266; |
| | | } |
| | | |
| | | .inline-input { |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .m-r-5 { |
| | | margin-right: 5px; |
| | | } |
| | | </style> |