From 36cacbaf78e510713002fcd5e3d61cece2e01421 Mon Sep 17 00:00:00 2001
From: quanwei <419654421@qq.com>
Date: Tue, 28 Oct 2025 18:43:26 +0800
Subject: [PATCH] 名片模板

---
 shop_vue/src/views/plus/business/template/edit.vue   | 1014 ++++++++
 admin/app/shop/model/plus/business/Business.php      |   12 
 admin/app/shop/model/plus/business/Industry.php      |   75 
 shop_vue/src/views/plus/business/grade/index.vue     |  278 ++
 shop_vue/src/views/plus/business/business/index.vue  |    7 
 mobile/pages/plus/business/components/visit-card.vue |  176 +
 admin/app/api/model/plus/business/Saving.php         |   10 
 admin/app/api/controller/plus/business/Industry.php  |   58 
 mobile/pages/plus/business/add.vue                   |  495 +++
 mobile/pages/plus/business/index.vue                 |  507 ++++
 shop_vue/src/views/plus/business/industry/Add.vue    |  104 
 admin/app/api/model/plus/business/Template.php       |    7 
 admin/app/api/controller/plus/business/Grade.php     |   17 
 admin/app/shop/model/plus/business/Grade.php         |   96 
 admin/app/api/model/plus/business/Business.php       |    7 
 admin/app/api/controller/plus/business/Template.php  |   17 
 admin/app/shop/controller/plus/business/Template.php |  195 +
 shop_vue/src/views/plus/business/template/add.vue    |  993 +++++++
 admin/app/api/controller/plus/business/Saving.php    |   17 
 shop_vue/src/views/plus/business/industry/Edit.vue   |  108 
 admin/app/shop/model/plus/business/Template.php      |   12 
 admin/app/common/model/plus/business/Business.php    |   93 
 admin/app/common/model/plus/business/Grade.php       |  104 
 admin/app/api/model/plus/business/Industry.php       |    9 
 mobile/pages/plus/business/share.vue                 |  390 +++
 mobile/pages/plus/business/detail.vue                |  448 +++
 shop_vue/src/views/plus/business/industry/index.vue  |  179 +
 shop_vue/src/views/plus/business/index.vue           |  135 +
 admin/app/common/service/business/Poster.php         |  431 +++
 shop_vue/src/api/business.js                         |   55 
 admin/app/common/model/plus/business/Industry.php    |  127 +
 admin/app/common/model/plus/business/Template.php    |   36 
 admin/app/shop/controller/plus/business/Industry.php |  115 
 admin/app/api/controller/plus/business/Business.php  |   17 
 mobile/pages/plus/business/information.vue           |  437 +++
 admin/app/common/model/plus/business/Saving.php      |   52 
 admin/app/shop/controller/plus/business/Business.php |   30 
 shop_vue/src/views/plus/business/template/index.vue  |  234 +
 admin/app/api/model/plus/business/Grade.php          |    7 
 mobile/pages/plus/business/visitors.vue              |  288 ++
 admin/app/shop/controller/plus/business/Grade.php    |   79 
 41 files changed, 7,471 insertions(+), 0 deletions(-)

diff --git a/admin/app/api/controller/plus/business/Business.php b/admin/app/api/controller/plus/business/Business.php
new file mode 100644
index 0000000..31a57cc
--- /dev/null
+++ b/admin/app/api/controller/plus/business/Business.php
@@ -0,0 +1,17 @@
+<?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());
+    }
+}
\ No newline at end of file
diff --git a/admin/app/api/controller/plus/business/Grade.php b/admin/app/api/controller/plus/business/Grade.php
new file mode 100644
index 0000000..955bb25
--- /dev/null
+++ b/admin/app/api/controller/plus/business/Grade.php
@@ -0,0 +1,17 @@
+<?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());
+    }
+}
\ No newline at end of file
diff --git a/admin/app/api/controller/plus/business/Industry.php b/admin/app/api/controller/plus/business/Industry.php
new file mode 100644
index 0000000..2dbab49
--- /dev/null
+++ b/admin/app/api/controller/plus/business/Industry.php
@@ -0,0 +1,58 @@
+<?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'));
+    }
+}
diff --git a/admin/app/api/controller/plus/business/Saving.php b/admin/app/api/controller/plus/business/Saving.php
new file mode 100644
index 0000000..b280126
--- /dev/null
+++ b/admin/app/api/controller/plus/business/Saving.php
@@ -0,0 +1,17 @@
+<?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());
+    }
+}
\ No newline at end of file
diff --git a/admin/app/api/controller/plus/business/Template.php b/admin/app/api/controller/plus/business/Template.php
new file mode 100644
index 0000000..90b9a93
--- /dev/null
+++ b/admin/app/api/controller/plus/business/Template.php
@@ -0,0 +1,17 @@
+<?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());
+    }
+}
diff --git a/admin/app/api/model/plus/business/Business.php b/admin/app/api/model/plus/business/Business.php
new file mode 100644
index 0000000..2926385
--- /dev/null
+++ b/admin/app/api/model/plus/business/Business.php
@@ -0,0 +1,7 @@
+<?php
+namespace app\api\model\plus\business;
+use app\common\model\plus\business\Business as CommonBusiness;
+class Business extends CommonBusiness
+{
+    
+}
\ No newline at end of file
diff --git a/admin/app/api/model/plus/business/Grade.php b/admin/app/api/model/plus/business/Grade.php
new file mode 100644
index 0000000..1a53b00
--- /dev/null
+++ b/admin/app/api/model/plus/business/Grade.php
@@ -0,0 +1,7 @@
+<?php
+namespace app\api\model\plus\business;
+use app\common\model\plus\business\Grade as CommonGrade;
+class Grade extends CommonGrade
+{
+    
+}
\ No newline at end of file
diff --git a/admin/app/api/model/plus/business/Industry.php b/admin/app/api/model/plus/business/Industry.php
new file mode 100644
index 0000000..b25188b
--- /dev/null
+++ b/admin/app/api/model/plus/business/Industry.php
@@ -0,0 +1,9 @@
+<?php
+namespace app\api\model\plus\business;
+
+use app\common\model\plus\business\Industry as CommonIndustry;
+
+class Industry extends CommonIndustry
+{
+    
+}
diff --git a/admin/app/api/model/plus/business/Saving.php b/admin/app/api/model/plus/business/Saving.php
new file mode 100644
index 0000000..a6bad33
--- /dev/null
+++ b/admin/app/api/model/plus/business/Saving.php
@@ -0,0 +1,10 @@
+<?php
+namespace app\api\model\plus\business;
+use app\common\model\plus\business\Saving as CommonSaving;
+/**
+ * 名片记录模型
+ */
+class Saving extends CommonSaving
+{
+    
+}
diff --git a/admin/app/api/model/plus/business/Template.php b/admin/app/api/model/plus/business/Template.php
new file mode 100644
index 0000000..938b3eb
--- /dev/null
+++ b/admin/app/api/model/plus/business/Template.php
@@ -0,0 +1,7 @@
+<?php
+namespace app\api\model\plus\business;
+use app\common\model\plus\business\Template as CommonTemplate;
+class Template extends CommonTemplate
+{
+    
+}
diff --git a/admin/app/common/model/plus/business/Business.php b/admin/app/common/model/plus/business/Business.php
new file mode 100644
index 0000000..19f7538
--- /dev/null
+++ b/admin/app/common/model/plus/business/Business.php
@@ -0,0 +1,93 @@
+<?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);
+    }
+}
\ No newline at end of file
diff --git a/admin/app/common/model/plus/business/Grade.php b/admin/app/common/model/plus/business/Grade.php
new file mode 100644
index 0000000..7674e9b
--- /dev/null
+++ b/admin/app/common/model/plus/business/Grade.php
@@ -0,0 +1,104 @@
+<?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();
+    }
+}
\ No newline at end of file
diff --git a/admin/app/common/model/plus/business/Industry.php b/admin/app/common/model/plus/business/Industry.php
new file mode 100644
index 0000000..9a1219f
--- /dev/null
+++ b/admin/app/common/model/plus/business/Industry.php
@@ -0,0 +1,127 @@
+<?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();
+    }
+}
\ No newline at end of file
diff --git a/admin/app/common/model/plus/business/Saving.php b/admin/app/common/model/plus/business/Saving.php
new file mode 100644
index 0000000..540df76
--- /dev/null
+++ b/admin/app/common/model/plus/business/Saving.php
@@ -0,0 +1,52 @@
+<?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();
+    }
+}
\ No newline at end of file
diff --git a/admin/app/common/model/plus/business/Template.php b/admin/app/common/model/plus/business/Template.php
new file mode 100644
index 0000000..97e3ece
--- /dev/null
+++ b/admin/app/common/model/plus/business/Template.php
@@ -0,0 +1,36 @@
+<?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();
+    }
+}
\ No newline at end of file
diff --git a/admin/app/common/service/business/Poster.php b/admin/app/common/service/business/Poster.php
new file mode 100644
index 0000000..310e18d
--- /dev/null
+++ b/admin/app/common/service/business/Poster.php
@@ -0,0 +1,431 @@
+<?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;
+    }
+
+}
\ No newline at end of file
diff --git a/admin/app/shop/controller/plus/business/Business.php b/admin/app/shop/controller/plus/business/Business.php
new file mode 100644
index 0000000..5c70cc8
--- /dev/null
+++ b/admin/app/shop/controller/plus/business/Business.php
@@ -0,0 +1,30 @@
+<?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('删除失败');
+    }
+}
\ No newline at end of file
diff --git a/admin/app/shop/controller/plus/business/Grade.php b/admin/app/shop/controller/plus/business/Grade.php
new file mode 100644
index 0000000..ce207a7
--- /dev/null
+++ b/admin/app/shop/controller/plus/business/Grade.php
@@ -0,0 +1,79 @@
+<?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'));
+    }
+}
\ No newline at end of file
diff --git a/admin/app/shop/controller/plus/business/Industry.php b/admin/app/shop/controller/plus/business/Industry.php
new file mode 100644
index 0000000..b076413
--- /dev/null
+++ b/admin/app/shop/controller/plus/business/Industry.php
@@ -0,0 +1,115 @@
+<?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'));
+    }
+}
\ No newline at end of file
diff --git a/admin/app/shop/controller/plus/business/Template.php b/admin/app/shop/controller/plus/business/Template.php
new file mode 100644
index 0000000..dd01ca6
--- /dev/null
+++ b/admin/app/shop/controller/plus/business/Template.php
@@ -0,0 +1,195 @@
+<?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('删除失败');
+    }
+
+}
\ No newline at end of file
diff --git a/admin/app/shop/model/plus/business/Business.php b/admin/app/shop/model/plus/business/Business.php
new file mode 100644
index 0000000..1e3444b
--- /dev/null
+++ b/admin/app/shop/model/plus/business/Business.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace app\shop\model\plus\business;
+use app\common\model\plus\business\Business as BusinessModel;
+
+/**
+ * 名片管理模型
+ */
+class Business extends BusinessModel
+{
+
+}
\ No newline at end of file
diff --git a/admin/app/shop/model/plus/business/Grade.php b/admin/app/shop/model/plus/business/Grade.php
new file mode 100644
index 0000000..48d2061
--- /dev/null
+++ b/admin/app/shop/model/plus/business/Grade.php
@@ -0,0 +1,96 @@
+<?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);
+    }
+}
diff --git a/admin/app/shop/model/plus/business/Industry.php b/admin/app/shop/model/plus/business/Industry.php
new file mode 100644
index 0000000..5a0ff4b
--- /dev/null
+++ b/admin/app/shop/model/plus/business/Industry.php
@@ -0,0 +1,75 @@
+<?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;
+        }
+    }
+}
diff --git a/admin/app/shop/model/plus/business/Template.php b/admin/app/shop/model/plus/business/Template.php
new file mode 100644
index 0000000..d3b1a29
--- /dev/null
+++ b/admin/app/shop/model/plus/business/Template.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace app\shop\model\plus\business;
+use app\common\model\plus\business\Template as TemplateModel;
+
+/**
+ * 名片模板
+ */
+class Template extends TemplateModel
+{
+
+}
\ No newline at end of file
diff --git a/mobile/pages/plus/business/add.vue b/mobile/pages/plus/business/add.vue
new file mode 100644
index 0000000..dc91edb
--- /dev/null
+++ b/mobile/pages/plus/business/add.vue
@@ -0,0 +1,495 @@
+<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>
\ No newline at end of file
diff --git a/mobile/pages/plus/business/components/visit-card.vue b/mobile/pages/plus/business/components/visit-card.vue
new file mode 100644
index 0000000..0afa9c5
--- /dev/null
+++ b/mobile/pages/plus/business/components/visit-card.vue
@@ -0,0 +1,176 @@
+<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>
\ No newline at end of file
diff --git a/mobile/pages/plus/business/detail.vue b/mobile/pages/plus/business/detail.vue
new file mode 100644
index 0000000..f0dbb02
--- /dev/null
+++ b/mobile/pages/plus/business/detail.vue
@@ -0,0 +1,448 @@
+<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>
\ No newline at end of file
diff --git a/mobile/pages/plus/business/index.vue b/mobile/pages/plus/business/index.vue
new file mode 100644
index 0000000..fc981c4
--- /dev/null
+++ b/mobile/pages/plus/business/index.vue
@@ -0,0 +1,507 @@
+<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>
\ No newline at end of file
diff --git a/mobile/pages/plus/business/information.vue b/mobile/pages/plus/business/information.vue
new file mode 100644
index 0000000..0850169
--- /dev/null
+++ b/mobile/pages/plus/business/information.vue
@@ -0,0 +1,437 @@
+<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>
\ No newline at end of file
diff --git a/mobile/pages/plus/business/share.vue b/mobile/pages/plus/business/share.vue
new file mode 100644
index 0000000..14c963c
--- /dev/null
+++ b/mobile/pages/plus/business/share.vue
@@ -0,0 +1,390 @@
+<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>
\ No newline at end of file
diff --git a/mobile/pages/plus/business/visitors.vue b/mobile/pages/plus/business/visitors.vue
new file mode 100644
index 0000000..b2b1703
--- /dev/null
+++ b/mobile/pages/plus/business/visitors.vue
@@ -0,0 +1,288 @@
+<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>
\ No newline at end of file
diff --git a/shop_vue/src/api/business.js b/shop_vue/src/api/business.js
new file mode 100644
index 0000000..2f12914
--- /dev/null
+++ b/shop_vue/src/api/business.js
@@ -0,0 +1,55 @@
+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;
\ No newline at end of file
diff --git a/shop_vue/src/views/plus/business/business/index.vue b/shop_vue/src/views/plus/business/business/index.vue
new file mode 100644
index 0000000..cee12a5
--- /dev/null
+++ b/shop_vue/src/views/plus/business/business/index.vue
@@ -0,0 +1,7 @@
+<template>
+
+</template>
+
+<style scoped>
+
+</style>
diff --git a/shop_vue/src/views/plus/business/grade/index.vue b/shop_vue/src/views/plus/business/grade/index.vue
new file mode 100644
index 0000000..87df71a
--- /dev/null
+++ b/shop_vue/src/views/plus/business/grade/index.vue
@@ -0,0 +1,278 @@
+<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>
\ No newline at end of file
diff --git a/shop_vue/src/views/plus/business/index.vue b/shop_vue/src/views/plus/business/index.vue
new file mode 100644
index 0000000..8462a52
--- /dev/null
+++ b/shop_vue/src/views/plus/business/index.vue
@@ -0,0 +1,135 @@
+<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>
diff --git a/shop_vue/src/views/plus/business/industry/Add.vue b/shop_vue/src/views/plus/business/industry/Add.vue
new file mode 100644
index 0000000..4fc8bb1
--- /dev/null
+++ b/shop_vue/src/views/plus/business/industry/Add.vue
@@ -0,0 +1,104 @@
+<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>
\ No newline at end of file
diff --git a/shop_vue/src/views/plus/business/industry/Edit.vue b/shop_vue/src/views/plus/business/industry/Edit.vue
new file mode 100644
index 0000000..b56d7a0
--- /dev/null
+++ b/shop_vue/src/views/plus/business/industry/Edit.vue
@@ -0,0 +1,108 @@
+<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>
\ No newline at end of file
diff --git a/shop_vue/src/views/plus/business/industry/index.vue b/shop_vue/src/views/plus/business/industry/index.vue
new file mode 100644
index 0000000..f1212c8
--- /dev/null
+++ b/shop_vue/src/views/plus/business/industry/index.vue
@@ -0,0 +1,179 @@
+<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>
\ No newline at end of file
diff --git a/shop_vue/src/views/plus/business/template/add.vue b/shop_vue/src/views/plus/business/template/add.vue
new file mode 100644
index 0000000..29d388e
--- /dev/null
+++ b/shop_vue/src/views/plus/business/template/add.vue
@@ -0,0 +1,993 @@
+<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>
\ No newline at end of file
diff --git a/shop_vue/src/views/plus/business/template/edit.vue b/shop_vue/src/views/plus/business/template/edit.vue
new file mode 100644
index 0000000..ec14b25
--- /dev/null
+++ b/shop_vue/src/views/plus/business/template/edit.vue
@@ -0,0 +1,1014 @@
+<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>
\ No newline at end of file
diff --git a/shop_vue/src/views/plus/business/template/index.vue b/shop_vue/src/views/plus/business/template/index.vue
new file mode 100644
index 0000000..dbb58d8
--- /dev/null
+++ b/shop_vue/src/views/plus/business/template/index.vue
@@ -0,0 +1,234 @@
+<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>
\ No newline at end of file

--
Gitblit v1.9.2