ソースを参照

新增客户部门 客户组织架构等

hurx 4 日 前
コミット
98e9b0e7d5

+ 63 - 0
src/api/company/sysCompany/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { SysCompanyVO, SysCompanyForm, SysCompanyQuery } from '@/api/company/sysCompany/types';
+
+/**
+ * 查询公司信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listSysCompany = (query?: SysCompanyQuery): AxiosPromise<SysCompanyVO[]> => {
+  return request({
+    url: '/system/sysCompany/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询公司信息详细
+ * @param id
+ */
+export const getSysCompany = (id: string | number): AxiosPromise<SysCompanyVO> => {
+  return request({
+    url: '/system/sysCompany/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增公司信息
+ * @param data
+ */
+export const addSysCompany = (data: SysCompanyForm) => {
+  return request({
+    url: '/system/sysCompany',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改公司信息
+ * @param data
+ */
+export const updateSysCompany = (data: SysCompanyForm) => {
+  return request({
+    url: '/system/sysCompany',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除公司信息
+ * @param id
+ */
+export const delSysCompany = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/sysCompany/' + id,
+    method: 'delete'
+  });
+};

+ 215 - 0
src/api/company/sysCompany/types.ts

@@ -0,0 +1,215 @@
+export interface SysCompanyVO {
+  /**
+   * ID
+   */
+  id: string | number;
+
+  /**
+   * 税务登记号/统一社会信用代码
+   */
+  taxNumber: string;
+
+  /**
+   * 公司编码
+   */
+  companyCode: string;
+
+  /**
+   * 公司名称
+   */
+  companyName: string;
+
+  /**
+   * 生效日期
+   */
+  beginDate: string;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital: number;
+
+  /**
+   * 公司全称
+   */
+  companyFullName: string;
+
+  /**
+   * 电子邮箱
+   */
+  email: string;
+
+  /**
+   * 失效日期
+   */
+  endDate: string;
+
+  /**
+   * 成立日期
+   */
+  foundedDate: string;
+
+  /**
+   * 注册日期(格式:YYYYMMDD)
+   */
+  registrationDate: string;
+
+  /**
+   * 是否显示
+   */
+  isShow: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface SysCompanyForm extends BaseEntity {
+  /**
+   * ID
+   */
+  id?: string | number;
+
+  /**
+   * 税务登记号/统一社会信用代码
+   */
+  taxNumber?: string;
+
+  /**
+   * 公司编码
+   */
+  companyCode?: string;
+
+  /**
+   * 公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 生效日期
+   */
+  beginDate?: string;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital?: number;
+
+  /**
+   * 公司全称
+   */
+  companyFullName?: string;
+
+  /**
+   * 电子邮箱
+   */
+  email?: string;
+
+  /**
+   * 失效日期
+   */
+  endDate?: string;
+
+  /**
+   * 成立日期
+   */
+  foundedDate?: string;
+
+  /**
+   * 注册日期(格式:YYYYMMDD)
+   */
+  registrationDate?: string;
+
+  /**
+   * 是否显示
+   */
+  isShow?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface SysCompanyQuery extends PageQuery {
+  /**
+   * 税务登记号/统一社会信用代码
+   */
+  taxNumber?: string;
+
+  /**
+   * 公司编码
+   */
+  companyCode?: string;
+
+  /**
+   * 公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 生效日期
+   */
+  beginDate?: string;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital?: number;
+
+  /**
+   * 公司全称
+   */
+  companyFullName?: string;
+
+  /**
+   * 电子邮箱
+   */
+  email?: string;
+
+  /**
+   * 失效日期
+   */
+  endDate?: string;
+
+  /**
+   * 成立日期
+   */
+  foundedDate?: string;
+
+  /**
+   * 注册日期(格式:YYYYMMDD)
+   */
+  registrationDate?: string;
+
+  /**
+   * 是否显示
+   */
+  isShow?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/customer/customerFile/customerDept/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { CustomerDeptVO, CustomerDeptForm, CustomerDeptQuery } from '@/api/customer/customerFile/customerDept/types';
+
+/**
+ * 查询客户部门信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listCustomerDept = (query?: CustomerDeptQuery): AxiosPromise<CustomerDeptVO[]> => {
+  return request({
+    url: '/customer/customerDept/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询客户部门信息详细
+ * @param id
+ */
+export const getCustomerDept = (id: string | number): AxiosPromise<CustomerDeptVO> => {
+  return request({
+    url: '/customer/customerDept/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增客户部门信息
+ * @param data
+ */
+export const addCustomerDept = (data: CustomerDeptForm) => {
+  return request({
+    url: '/customer/customerDept',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改客户部门信息
+ * @param data
+ */
+export const updateCustomerDept = (data: CustomerDeptForm) => {
+  return request({
+    url: '/customer/customerDept',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除客户部门信息
+ * @param id
+ */
+export const delCustomerDept = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/customer/customerDept/' + id,
+    method: 'delete'
+  });
+};

+ 325 - 0
src/api/customer/customerFile/customerDept/types.ts

@@ -0,0 +1,325 @@
+export interface CustomerDeptVO {
+  /**
+   * ID
+   */
+  id: string | number;
+
+  /**
+   * 部门编号
+   */
+  deptNo: string;
+
+  /**
+   * 部门名称
+   */
+  deptName: string;
+
+  /**
+   * 父部门id
+   */
+  parentId: string | number;
+
+  /**
+   * 祖级列表
+   */
+  ancestors: string;
+
+  /**
+   * 客户编号
+   */
+  customerId: string | number;
+
+  /**
+   * 部门层级(如1级、2级等)
+   */
+  departmentLevel: number;
+
+  /**
+   * 年度预算
+   */
+  yearlyBudget: number;
+
+  /**
+   * 已使用预算
+   */
+  usedBudget: number;
+
+  /**
+   * 月度限额
+   */
+  monthLimit: number;
+
+  /**
+   * 月度已用预算
+   */
+  monthUsedBudget: number;
+
+  /**
+   * 绑定状态
+   */
+  bindStatus: string;
+
+  /**
+   * 绑定地址
+   */
+  bindAddress: string;
+
+  /**
+   * 部门负责人
+   */
+  deptManage: string;
+
+  /**
+   * 是否限制预算
+   */
+  isLimit: string;
+
+  /**
+   * 所选年份
+   */
+  selectYear: string;
+
+  /**
+   * 费用类型
+   */
+  expenseType: string;
+
+  /**
+   * 年度剩余预算
+   */
+  residueYearlyBudget: string | number;
+
+  /**
+   * 充值金额
+   */
+  recharge: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 子对象
+   */
+  children: CustomerDeptVO[];
+}
+
+export interface CustomerDeptForm extends BaseEntity {
+  /**
+   * ID
+   */
+  id?: string | number;
+
+  /**
+   * 部门编号
+   */
+  deptNo?: string;
+
+  /**
+   * 部门名称
+   */
+  deptName?: string;
+
+  /**
+   * 父部门id
+   */
+  parentId?: string | number;
+
+  /**
+   * 祖级列表
+   */
+  ancestors?: string;
+
+  /**
+   * 客户编号
+   */
+  customerId?: string | number;
+
+  /**
+   * 部门层级(如1级、2级等)
+   */
+  departmentLevel?: number;
+
+  /**
+   * 年度预算
+   */
+  yearlyBudget?: number;
+
+  /**
+   * 已使用预算
+   */
+  usedBudget?: number;
+
+  /**
+   * 月度限额
+   */
+  monthLimit?: number;
+
+  /**
+   * 月度已用预算
+   */
+  monthUsedBudget?: number;
+
+  /**
+   * 绑定状态
+   */
+  bindStatus?: string;
+
+  /**
+   * 绑定地址
+   */
+  bindAddress?: string;
+
+  /**
+   * 部门负责人
+   */
+  deptManage?: string;
+
+  /**
+   * 是否限制预算
+   */
+  isLimit?: string;
+
+  /**
+   * 所选年份
+   */
+  selectYear?: string;
+
+  /**
+   * 费用类型
+   */
+  expenseType?: string;
+
+  /**
+   * 年度剩余预算
+   */
+  residueYearlyBudget?: string | number;
+
+  /**
+   * 充值金额
+   */
+  recharge?: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface CustomerDeptQuery {
+  /**
+   * 部门编号
+   */
+  deptNo?: string;
+
+  /**
+   * 部门名称
+   */
+  deptName?: string;
+
+  /**
+   * 父部门id
+   */
+  parentId?: string | number;
+
+  /**
+   * 祖级列表
+   */
+  ancestors?: string;
+
+  /**
+   * 客户编号
+   */
+  customerId?: string | number;
+
+  /**
+   * 部门层级(如1级、2级等)
+   */
+  departmentLevel?: number;
+
+  /**
+   * 年度预算
+   */
+  yearlyBudget?: number;
+
+  /**
+   * 已使用预算
+   */
+  usedBudget?: number;
+
+  /**
+   * 月度限额
+   */
+  monthLimit?: number;
+
+  /**
+   * 月度已用预算
+   */
+  monthUsedBudget?: number;
+
+  /**
+   * 绑定状态
+   */
+  bindStatus?: string;
+
+  /**
+   * 绑定地址
+   */
+  bindAddress?: string;
+
+  /**
+   * 部门负责人
+   */
+  deptManage?: string;
+
+  /**
+   * 是否限制预算
+   */
+  isLimit?: string;
+
+  /**
+   * 所选年份
+   */
+  selectYear?: string;
+
+  /**
+   * 费用类型
+   */
+  expenseType?: string;
+
+  /**
+   * 年度剩余预算
+   */
+  residueYearlyBudget?: string | number;
+
+  /**
+   * 充值金额
+   */
+  recharge?: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 1 - 0
src/api/customer/customerFile/customerInfo/types.ts

@@ -372,6 +372,7 @@ export interface CustomerInfoQuery extends PageQuery {
   serviceStaffId?: string | number;
 
   belongingDepartmentId?: string | number;
+
   /**
    * 固定电话
    */

+ 63 - 0
src/api/customer/customerFile/groupCustomer/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { GroupCustomerVO, GroupCustomerForm, GroupCustomerQuery } from '@/api/customer/customerFile/groupCustomer/types';
+
+/**
+ * 查询集团客户关系列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listGroupCustomer = (query?: GroupCustomerQuery): AxiosPromise<GroupCustomerVO[]> => {
+  return request({
+    url: '/customer/groupCustomer/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询集团客户关系详细
+ * @param id
+ */
+export const getGroupCustomer = (id: string | number): AxiosPromise<GroupCustomerVO> => {
+  return request({
+    url: '/customer/groupCustomer/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增集团客户关系
+ * @param data
+ */
+export const addGroupCustomer = (data: GroupCustomerForm) => {
+  return request({
+    url: '/customer/groupCustomer',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改集团客户关系
+ * @param data
+ */
+export const updateGroupCustomer = (data: GroupCustomerForm) => {
+  return request({
+    url: '/customer/groupCustomer',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除集团客户关系
+ * @param id
+ */
+export const delGroupCustomer = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/customer/groupCustomer/' + id,
+    method: 'delete'
+  });
+};

+ 95 - 0
src/api/customer/customerFile/groupCustomer/types.ts

@@ -0,0 +1,95 @@
+export interface GroupCustomerVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 集团客户id
+   */
+  groupCustomerId: string | number;
+
+  /**
+   * 客户id
+   */
+  customerId: string | number;
+
+  /**
+   * 客户等级
+   */
+  customerLevel: string;
+
+  /**
+   * 顶级客户id
+   */
+  topCustomerId: string | number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface GroupCustomerForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 集团客户id
+   */
+  groupCustomerId?: string | number;
+
+  /**
+   * 客户id
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户等级
+   */
+  customerLevel?: string;
+
+  /**
+   * 顶级客户id
+   */
+  topCustomerId?: string | number;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface GroupCustomerQuery extends PageQuery {
+  /**
+   * 集团客户id
+   */
+  groupCustomerId?: string | number;
+
+  /**
+   * 客户id
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户等级
+   */
+  customerLevel?: string;
+
+  /**
+   * 顶级客户id
+   */
+  topCustomerId?: string | number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/customer/invoiceType/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { InvoiceTypeVO, InvoiceTypeForm, InvoiceTypeQuery } from '@/api/system/invoiceType/types';
+
+/**
+ * 查询发票类型列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listInvoiceType = (query?: InvoiceTypeQuery): AxiosPromise<InvoiceTypeVO[]> => {
+  return request({
+    url: '/system/invoiceType/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询发票类型详细
+ * @param id
+ */
+export const getInvoiceType = (id: string | number): AxiosPromise<InvoiceTypeVO> => {
+  return request({
+    url: '/system/invoiceType/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增发票类型
+ * @param data
+ */
+export const addInvoiceType = (data: InvoiceTypeForm) => {
+  return request({
+    url: '/system/invoiceType',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改发票类型
+ * @param data
+ */
+export const updateInvoiceType = (data: InvoiceTypeForm) => {
+  return request({
+    url: '/system/invoiceType',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除发票类型
+ * @param id
+ */
+export const delInvoiceType = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/invoiceType/' + id,
+    method: 'delete'
+  });
+};

+ 95 - 0
src/api/customer/invoiceType/types.ts

@@ -0,0 +1,95 @@
+export interface InvoiceTypeVO {
+  /**
+   * ID
+   */
+  id: string | number;
+
+  /**
+   * 发票类型编号
+   */
+  invoiceTypeNo: string;
+
+  /**
+   * 发票类型名称
+   */
+  invoiceTypeName: string;
+
+  /**
+   * 是否显示:0-显示,1-隐藏
+   */
+  isShow: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface InvoiceTypeForm extends BaseEntity {
+  /**
+   * ID
+   */
+  id?: string | number;
+
+  /**
+   * 发票类型编号
+   */
+  invoiceTypeNo?: string;
+
+  /**
+   * 发票类型名称
+   */
+  invoiceTypeName?: string;
+
+  /**
+   * 是否显示:0-显示,1-隐藏
+   */
+  isShow?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface InvoiceTypeQuery extends PageQuery {
+  /**
+   * 发票类型编号
+   */
+  invoiceTypeNo?: string;
+
+  /**
+   * 发票类型名称
+   */
+  invoiceTypeName?: string;
+
+  /**
+   * 是否显示:0-显示,1-隐藏
+   */
+  isShow?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 54 - 0
src/api/system/dept/types.ts

@@ -5,6 +5,7 @@ export interface DeptQuery extends PageQuery {
   deptName?: string;
   deptCategory?: string;
   status?: number;
+  customerId?: number | string;
 }
 
 /**
@@ -26,6 +27,33 @@ export interface DeptVO extends BaseEntity {
   delFlag: string;
   ancestors: string;
   menuId: string | number;
+
+  /**
+   * 年度预算
+   */
+  annualBudget: number;
+
+  /**
+   * 已用预算
+   */
+  budgetUsed: number;
+
+  /**
+   * 月采限额
+   */
+  monthlyQuota: number;
+
+  /**
+   * 是否绑定
+   */
+  isBound: string;
+
+  /**
+   * 地址ID
+   */
+  shopAddressId: number | string;
+
+  customerId: number | string;
 }
 
 /**
@@ -38,6 +66,7 @@ export interface DeptTreeVO extends BaseEntity {
   weight: number;
   children: DeptTreeVO[];
   disabled: boolean;
+  customerId: number | string;
 }
 
 /**
@@ -57,4 +86,29 @@ export interface DeptForm {
   status?: string;
   delFlag?: string;
   ancestors?: string;
+  /**
+   * 年度预算
+   */
+  annualBudget?: number;
+
+  /**
+   * 已用预算
+   */
+  budgetUsed?: number;
+
+  /**
+   * 月采限额
+   */
+  monthlyQuota?: number;
+
+  /**
+   * 是否绑定
+   */
+  isBound?: string;
+
+  /**
+   * 地址ID
+   */
+  shopAddressId?: number | string;
+  customerId?: number | string;
 }

+ 161 - 17
src/views/customer/customerFile/customerInfo/add.vue

@@ -13,9 +13,7 @@
           <el-col :span="8">
             <el-form-item label="所属公司" prop="belongCompanyId">
               <el-select v-model="form.belongCompanyId" placeholder="请选择所属公司" class="w-full" filterable>
-                <el-option label="公司A" value="1" />
-                <el-option label="公司B" value="2" />
-                <el-option label="公司C" value="3" />
+                <el-option v-for="item in companyList" :key="item.id" :label="`${item.companyCode} , ${item.companyName}`" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -40,9 +38,7 @@
           <el-col :span="8">
             <el-form-item label="开票类型" prop="invoiceTypeId">
               <el-select v-model="form.invoiceTypeId" placeholder="请选择开票类型" class="w-full">
-                <el-option label="增值税专用发票" value="1" />
-                <el-option label="增值税普通发票" value="2" />
-                <el-option label="电子发票" value="3" />
+                <el-option v-for="item in invoiceTypeList" :key="item.id" :label="item.invoiceTypeName" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -110,7 +106,14 @@
           </el-col>
           <el-col :span="8">
             <el-form-item label="结束时间" prop="validityToDate">
-              <el-date-picker v-model="form.validityToDate" type="date" placeholder="请选择结束时间" class="w-full" style="width: 100%" />
+              <el-date-picker
+                v-model="form.validityToDate"
+                type="date"
+                placeholder="请选择结束时间"
+                class="w-full"
+                style="width: 100%"
+                :disabled-date="(time) => form.validityFromDate && time.getTime() < new Date(form.validityFromDate).getTime()"
+              />
             </el-form-item>
           </el-col>
         </el-row>
@@ -122,12 +125,12 @@
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item label="详细地址">
+            <el-form-item label="详细地址" prop="address">
               <el-cascader v-model="codeArr" :options="regionData" placeholder="请选择" @change="handleChange" style="width: 100%"></el-cascader>
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item prop="address">
+            <el-form-item>
               <el-input v-model="form.address" placeholder="请输入详细地址" />
             </el-form-item>
           </el-col>
@@ -203,17 +206,65 @@
           </el-col>
         </el-row>
         <el-row :gutter="20">
-          <el-col :span="8">
+          <el-col :span="24">
             <el-form-item label="营业执照" prop="businessLicense">
-              <el-upload action="#" :auto-upload="false">
-                <el-button icon="Plus">上传</el-button>
-              </el-upload>
+              <div
+                v-if="!businessForm.businessLicense"
+                class="upload-box"
+                @click="businessLicenseSelectorVisible = true"
+                style="
+                  width: 360px;
+                  height: 200px;
+                  border: 2px dashed #d9d9d9;
+                  border-radius: 4px;
+                  display: flex;
+                  align-items: center;
+                  justify-content: center;
+                  cursor: pointer;
+                  transition: all 0.3s;
+                "
+                @mouseenter="(e) => (e.currentTarget.style.borderColor = '#409eff')"
+                @mouseleave="(e) => (e.currentTarget.style.borderColor = '#d9d9d9')"
+              >
+                <div style="text-align: center; color: #8c939d">
+                  <el-icon :size="40" style="margin-bottom: 8px">
+                    <Plus />
+                  </el-icon>
+                  <div style="font-size: 14px">点击上传</div>
+                </div>
+              </div>
+              <div v-else style="position: relative; display: inline-block">
+                <el-image
+                  :src="businessForm.businessLicense"
+                  style="width: 360px; height: 200px"
+                  fit="contain"
+                  :preview-src-list="[businessForm.businessLicense]"
+                />
+                <el-button
+                  type="danger"
+                  :icon="Delete"
+                  circle
+                  size="small"
+                  style="position: absolute; top: 5px; right: 5px"
+                  @click="businessForm.businessLicense = ''"
+                />
+              </div>
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
     </el-card>
 
+    <!-- 营业执照选择器对话框 -->
+    <FileSelector
+      v-model="businessLicenseSelectorVisible"
+      title="选择营业执照"
+      :allowed-types="[1]"
+      :multiple="false"
+      :allow-upload="true"
+      @confirm="handleBusinessLicenseSelected"
+    />
+
     <!-- 联系人信息列表 -->
     <el-card shadow="never" class="mb-4">
       <template #header>
@@ -251,7 +302,7 @@
       <template #header>
         <div class="flex justify-between items-center">
           <span class="font-medium">销售信息</span>
-          <el-button type="primary" @click="handleSubmit" :loading="submitLoading">保存</el-button>
+          <el-button type="primary" @click="handleSubmit">保存</el-button>
         </div>
       </template>
       <el-form :model="salesForm" label-width="120px">
@@ -324,8 +375,11 @@
 </template>
 
 <script setup lang="ts" name="CustomerInfoAdd">
-import { listCustomerInfo, getCustomerInfo, delCustomerInfo, addCustomerInfo, updateCustomerInfo } from '@/api/customer/customerFile/customerInfo';
-import { CustomerInfoVO, CustomerInfoQuery, CustomerInfoForm } from '@/api/customer/customerFile/customerInfo/types';
+import { listCustomerInfo, getCustomerInfo, addCustomerInfo } from '@/api/customer/customerFile/customerInfo';
+import { CustomerInfoForm } from '@/api/customer/customerFile/customerInfo/types';
+import { listInvoiceType } from '@/api/customer/invoiceType';
+import { InvoiceTypeVO, InvoiceTypeQuery } from '@/api/customer/invoiceType/types';
+import FileSelector from '@/components/FileSelector/index.vue';
 import { generateCustomerNumber } from '@/utils/customerNumber';
 import type { CustomerContactForm } from '@/api/customer/customerFile/customerContact/types';
 import type { InvoiceInfoForm } from '@/api/customer/customerFile/invoiceInfo/types';
@@ -338,6 +392,9 @@ import { listEnterpriseScale } from '@/api/customer/customerCategory/enterpriseS
 import { listIndustryCategory } from '@/api/customer/customerCategory/industryCategory';
 import type { EnterpriseScaleVO } from '@/api/customer/customerCategory/enterpriseScale/types';
 import type { IndustryCategoryVO } from '@/api/customer/customerCategory/industryCategory/types';
+import { listSysCompany } from '@/api/company/sysCompany';
+import type { SysCompanyVO } from '@/api/company/sysCompany/types';
+import { Plus, Delete } from '@element-plus/icons-vue';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { customer_type, customer_level } = toRefs<any>(proxy?.useDict('customer_type', 'customer_level'));
@@ -357,12 +414,70 @@ const codeArr = ref([]);
 // 下拉框数据列表
 const enterpriseScaleList = ref<EnterpriseScaleVO[]>([]);
 const industryCategoryList = ref<IndustryCategoryVO[]>([]);
+const invoiceTypeList = ref<InvoiceTypeVO[]>([]);
+const companyList = ref<SysCompanyVO[]>([]);
+
+// Logo选择器相关
+const logoSelectorVisible = ref(false);
+const businessLicenseSelectorVisible = ref(false);
+
+// Logo选择处理
+const handleLogoSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    const file = files[0]; // 取第一个文件
+    if (file && (file.url || file.path)) {
+      //   form.value.logo = file.url || file.path;
+      ElMessage.success('Logo选择成功');
+
+      // 选择完成后清理表单验证状态
+      nextTick(() => {
+        formRef.value?.clearValidate();
+      });
+    } else {
+      ElMessage.error('请选择有效的图片文件');
+    }
+  } else {
+    ElMessage.error('请选择有效的图片文件');
+  }
+};
+
+// 营业执照选择处理
+const handleBusinessLicenseSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    const file = files[0]; // 取第一个文件
+    if (file && (file.url || file.path)) {
+      businessForm.businessLicense = file.url || file.path;
+      ElMessage.success('营业执照选择成功');
+
+      // 选择完成后清理表单验证状态
+      nextTick(() => {
+        formRef.value?.clearValidate('businessLicense');
+      });
+    } else {
+      ElMessage.error('请选择有效的图片文件');
+    }
+  } else {
+    ElMessage.error('请选择有效的图片文件');
+  }
+};
+
+// 监听对话框关闭,清理表单验证状态
+watch(logoSelectorVisible, (newVal) => {
+  if (!newVal) {
+    // 对话框关闭后清理可能的表单验证状态
+    nextTick(() => {
+      formRef.value?.clearValidate();
+    });
+  }
+});
 
 // 初始化
 onMounted(async () => {
   // 加载下拉框数据
   await loadEnterpriseScaleList();
   await loadIndustryCategoryList();
+  await loadInvoiceTypeList();
+  await loadCompanyList();
 
   // 判断是新增还是编辑
   const id = route.query.id;
@@ -437,6 +552,26 @@ const loadIndustryCategoryList = async () => {
   }
 };
 
+// 加载开票类型列表
+const loadInvoiceTypeList = async () => {
+  try {
+    const res = await listInvoiceType();
+    invoiceTypeList.value = res.rows || [];
+  } catch (error) {
+    console.error('加载开票类型列表失败:', error);
+  }
+};
+
+// 加载公司列表
+const loadCompanyList = async () => {
+  try {
+    const res = await listSysCompany();
+    companyList.value = res.rows || [];
+  } catch (error) {
+    console.error('加载公司列表失败:', error);
+  }
+};
+
 // 企业基本信息表单
 const form = reactive<CustomerInfoForm>({
   customerNo: '',
@@ -499,7 +634,16 @@ const currentInvoiceIndex = ref<number>(-1);
 
 // 表单验证规则
 const rules = {
-  companyName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }]
+  belongCompanyId: [{ required: true, message: '请选择所属公司', trigger: 'change' }],
+  customerName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
+  businessCustomerName: [{ required: true, message: '请输入工商名称', trigger: 'blur' }],
+  shortName: [{ required: true, message: '请输入企业简称', trigger: 'blur' }],
+  invoiceTypeId: [{ required: true, message: '请选择开票类型', trigger: 'change' }],
+  enterpriseScaleId: [{ required: true, message: '请选择企业规模', trigger: 'change' }],
+  customerTypeId: [{ required: true, message: '请选择客户类别', trigger: 'change' }],
+  industryCategoryId: [{ required: true, message: '请选择行业类别', trigger: 'change' }],
+  customerLevelId: [{ required: true, message: '请选择客户等级', trigger: 'change' }],
+  address: [{ required: true, message: '请输入详细地址', trigger: 'blur' }]
 };
 
 // 获取角色名称

+ 110 - 22
src/views/customer/customerFile/customerInfo/overview/baseInfo.vue

@@ -15,9 +15,7 @@
           <el-col :span="8">
             <el-form-item label="所属公司" prop="belongCompanyId">
               <el-select v-model="form.belongCompanyId" placeholder="请选择所属公司" class="w-full" filterable>
-                <el-option label="公司A" value="1" />
-                <el-option label="公司B" value="2" />
-                <el-option label="公司C" value="3" />
+                <el-option v-for="item in companyList" :key="item.id" :label="`${item.companyCode} , ${item.companyName}`" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -42,9 +40,7 @@
           <el-col :span="8">
             <el-form-item label="开票类型" prop="invoiceTypeId">
               <el-select v-model="form.invoiceTypeId" placeholder="请选择开票类型" class="w-full">
-                <el-option label="增值税专用发票" value="1" />
-                <el-option label="增值税普通发票" value="2" />
-                <el-option label="电子发票" value="3" />
+                <el-option v-for="item in invoiceTypeList" :key="item.id" :label="item.invoiceTypeName" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -126,6 +122,7 @@
                 class="w-full"
                 value-format="YYYY-MM-DD"
                 style="width: 100%"
+                :disabled-date="(time) => form.validityFromDate && time.getTime() < new Date(form.validityFromDate).getTime()"
               />
             </el-form-item>
           </el-col>
@@ -226,21 +223,46 @@
         <el-row :gutter="20">
           <el-col :span="24">
             <el-form-item label="营业执照">
-              <div class="flex items-center gap-2">
-                <el-upload
-                  ref="uploadRef"
-                  :action="uploadUrl"
-                  :headers="uploadHeaders"
-                  :on-success="handleUploadSuccess"
-                  :on-error="handleUploadError"
-                  :before-upload="beforeUpload"
-                  :show-file-list="false"
-                  accept="image/*,.pdf"
-                >
-                  <el-button icon="Plus" :loading="uploadLoading">上传</el-button>
-                </el-upload>
-                <span v-if="businessInfo.businessLicense" class="text-sm text-gray-500">{{ businessInfo.businessLicense }}</span>
-                <el-button v-if="businessInfo.businessLicense" link type="primary" @click="previewLicense">预览</el-button>
+              <div
+                v-if="!businessInfo.businessLicense"
+                class="upload-box"
+                @click="businessLicenseSelectorVisible = true"
+                style="
+                  width: 360px;
+                  height: 200px;
+                  border: 2px dashed #d9d9d9;
+                  border-radius: 4px;
+                  display: flex;
+                  align-items: center;
+                  justify-content: center;
+                  cursor: pointer;
+                  transition: all 0.3s;
+                "
+                @mouseenter="(e) => (e.currentTarget.style.borderColor = '#409eff')"
+                @mouseleave="(e) => (e.currentTarget.style.borderColor = '#d9d9d9')"
+              >
+                <div style="text-align: center; color: #8c939d">
+                  <el-icon :size="40" style="margin-bottom: 8px">
+                    <Plus />
+                  </el-icon>
+                  <div style="font-size: 14px">点击上传</div>
+                </div>
+              </div>
+              <div v-else style="position: relative; display: inline-block">
+                <el-image
+                  :src="businessInfo.businessLicense"
+                  style="width: 360px; height: 200px"
+                  fit="contain"
+                  :preview-src-list="[businessInfo.businessLicense]"
+                />
+                <el-button
+                  type="danger"
+                  :icon="Delete"
+                  circle
+                  size="small"
+                  style="position: absolute; top: 5px; right: 5px"
+                  @click="businessInfo.businessLicense = ''"
+                />
               </div>
             </el-form-item>
           </el-col>
@@ -378,6 +400,16 @@
 
     <!-- 添加/编辑开票信息对话框 -->
     <add-invoice-dialog v-model="invoiceDialogVisible" :edit-data="currentInvoice" @confirm="handleInvoiceConfirm" />
+
+    <!-- 营业执照选择器对话框 -->
+    <FileSelector
+      v-model="businessLicenseSelectorVisible"
+      title="选择营业执照"
+      :allowed-types="[1]"
+      :multiple="false"
+      :allow-upload="true"
+      @confirm="handleBusinessLicenseSelected"
+    />
   </div>
 </template>
 
@@ -391,8 +423,15 @@ import AddInvoiceDialog from '../components/addInvoiceDialog.vue';
 import { regionData } from 'element-china-area-data';
 import { listEnterpriseScale } from '@/api/customer/customerCategory/enterpriseScale';
 import { listIndustryCategory } from '@/api/customer/customerCategory/industryCategory';
+import { listInvoiceType } from '@/api/customer/invoiceType';
 import type { EnterpriseScaleVO } from '@/api/customer/customerCategory/enterpriseScale/types';
 import type { IndustryCategoryVO } from '@/api/customer/customerCategory/industryCategory/types';
+import type { InvoiceTypeVO } from '@/api/customer/invoiceType/types';
+import { listSysCompany } from '@/api/company/sysCompany';
+import type { SysCompanyVO } from '@/api/company/sysCompany/types';
+import FileSelector from '@/components/FileSelector/index.vue';
+import { Plus, Delete } from '@element-plus/icons-vue';
+
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { order_check_way, customer_type, customer_level } = toRefs<any>(proxy?.useDict('order_check_way', 'customer_type', 'customer_level'));
 // 接收父组件传递的props
@@ -413,6 +452,8 @@ const codeArr = ref([]);
 // 下拉框数据列表
 const enterpriseScaleList = ref<EnterpriseScaleVO[]>([]);
 const industryCategoryList = ref<IndustryCategoryVO[]>([]);
+const invoiceTypeList = ref<InvoiceTypeVO[]>([]);
+const companyList = ref<SysCompanyVO[]>([]);
 
 // 企业基本信息(用于显示)
 const customerInfo = reactive<any>({
@@ -506,6 +547,7 @@ const currentInvoiceIndex = ref<number>(-1);
 // 营业执照上传相关
 const uploadRef = ref();
 const uploadLoading = ref(false);
+const businessLicenseSelectorVisible = ref(false);
 const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/common/upload';
 const uploadHeaders = ref({
   Authorization: 'Bearer ' + localStorage.getItem('token')
@@ -513,7 +555,16 @@ const uploadHeaders = ref({
 
 // 表单验证规则
 const rules = {
-  companyName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }]
+  belongCompanyId: [{ required: true, message: '请选择所属公司', trigger: 'change' }],
+  customerName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
+  businessCustomerName: [{ required: true, message: '请输入工商名称', trigger: 'blur' }],
+  shortName: [{ required: true, message: '请输入企业简称', trigger: 'blur' }],
+  invoiceTypeId: [{ required: true, message: '请选择开票类型', trigger: 'change' }],
+  enterpriseScaleId: [{ required: true, message: '请选择企业规模', trigger: 'change' }],
+  customerTypeId: [{ required: true, message: '请选择客户类别', trigger: 'change' }],
+  industryCategoryId: [{ required: true, message: '请选择行业类别', trigger: 'change' }],
+  customerLevelId: [{ required: true, message: '请选择客户等级', trigger: 'change' }],
+  address: [{ required: true, message: '请输入详细地址', trigger: 'blur' }]
 };
 
 // 初始化
@@ -521,6 +572,8 @@ onMounted(async () => {
   // 加载下拉框数据
   await loadEnterpriseScaleList();
   await loadIndustryCategoryList();
+  await loadInvoiceTypeList();
+  await loadCompanyList();
 
   // 优先使用props传递的customerId,否则从路由获取
   const id = props.customerId || route.query.id;
@@ -561,6 +614,26 @@ const loadIndustryCategoryList = async () => {
   }
 };
 
+// 加载开票类型列表
+const loadInvoiceTypeList = async () => {
+  try {
+    const res = await listInvoiceType();
+    invoiceTypeList.value = res.rows || [];
+  } catch (error) {
+    console.error('加载开票类型列表失败:', error);
+  }
+};
+
+// 加载公司列表
+const loadCompanyList = async () => {
+  try {
+    const res = await listSysCompany();
+    companyList.value = res.rows || [];
+  } catch (error) {
+    console.error('加载公司列表失败:', error);
+  }
+};
+
 // 加载客户数据
 const loadCustomerData = async (id: string) => {
   try {
@@ -754,6 +827,21 @@ const previewLicense = () => {
   }
 };
 
+// 营业执照选择处理
+const handleBusinessLicenseSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    const file = files[0]; // 取第一个文件
+    if (file && (file.url || file.path)) {
+      businessInfo.businessLicense = file.url || file.path;
+      ElMessage.success('营业执照选择成功');
+    } else {
+      ElMessage.error('请选择有效的图片文件');
+    }
+  } else {
+    ElMessage.error('请选择有效的图片文件');
+  }
+};
+
 // 编辑开票信息
 const handleEditInvoice = (row: InvoiceInfoForm, index: number) => {
   currentInvoice.value = { ...row };

+ 34 - 5
src/views/customer/customerFile/customerInfo/overview/contactInfo.vue

@@ -76,10 +76,17 @@
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="所属部门" prop="deptId">
-              <el-select v-model="form.deptId" placeholder="请选择" class="w-full">
-                <el-option label="部门1" value="1" />
-                <el-option label="部门2" value="2" />
-              </el-select>
+              <el-tree-select
+                v-model="form.deptId"
+                :data="customerDeptList"
+                :props="{ value: 'id', label: 'deptName', children: 'children' }"
+                value-key="id"
+                placeholder="请选择"
+                check-strictly
+                :render-after-expand="false"
+                filterable
+                style="width: 100%"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -144,6 +151,8 @@ import {
   updateCustomerContact
 } from '@/api/customer/customerFile/customerContact';
 import { CustomerContactVO, CustomerContactQuery, CustomerContactForm } from '@/api/customer/customerFile/customerContact/types';
+import { listCustomerDept, getCustomerDept } from '@/api/customer/customerFile/customerDept';
+import { CustomerDeptVO, CustomerDeptQuery } from '@/api/customer/customerFile/customerDept/types';
 import { regionData } from 'element-china-area-data';
 // 接收父组件传递的props
 const props = defineProps<{
@@ -156,6 +165,7 @@ const { sys_platform_yes_no, sys_user_sex, sys_normal_disable } = toRefs<any>(
 );
 
 const customerContactList = ref<CustomerContactVO[]>([]);
+const customerDeptList = ref<CustomerDeptVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const showSearch = ref(true);
@@ -237,6 +247,21 @@ const getList = async () => {
   loading.value = false;
 };
 
+/** 加载客户部门列表 */
+const loadCustomerDeptList = async () => {
+  if (!props.customerId) return;
+  try {
+    const query: CustomerDeptQuery = {
+      customerId: props.customerId
+    };
+    const res = await listCustomerDept(query);
+    // 将数据转换为树形结构
+    customerDeptList.value = proxy?.handleTree<CustomerDeptVO>(res.data, 'id', 'parentId') || [];
+  } catch (error) {
+    console.error('加载部门列表失败:', error);
+  }
+};
+
 /** 取消按钮 */
 const cancel = () => {
   reset();
@@ -304,8 +329,10 @@ const handleSelectionChange = (selection: CustomerContactVO[]) => {
 };
 
 /** 新增按钮操作 */
-const handleAdd = () => {
+const handleAdd = async () => {
   reset();
+  // 加载部门列表
+  await loadCustomerDeptList();
   // 设置客户ID
   if (props.customerId) {
     form.value.customerId = props.customerId;
@@ -317,6 +344,8 @@ const handleAdd = () => {
 /** 修改按钮操作 */
 const handleUpdate = async (row?: CustomerContactVO) => {
   reset();
+  // 加载部门列表
+  await loadCustomerDeptList();
   const _id = row?.id || ids.value[0];
   const res = await getCustomerContact(_id);
   // 如果有省市区编码,回显到级联选择器

+ 244 - 0
src/views/customer/customerFile/customerInfo/overview/orgStructure copy.vue

@@ -0,0 +1,244 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="22">
+            <span>企业组织结构信息列表</span>
+          </el-col>
+          <el-col :span="2" style="text-align: right">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd">新增部门</el-button>
+          </el-col>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="deptList" row-key="deptId" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
+        <el-table-column prop="deptName" label="部门名称" width="200" />
+        <el-table-column prop="annualBudget" label="年度预算" align="center" />
+        <el-table-column prop="monthlyQuota" label="月采限额" align="center" />
+        <el-table-column prop="budgetUsed" label="已用预算" align="center" />
+        <el-table-column prop="shopAddressId" label="绑定地址" align="center" />
+        <el-table-column label="状态" align="center">
+          <template #default="scope">
+            <el-tag v-if="scope.row.status === '0'" type="success">启用</el-tag>
+            <el-tag v-else type="info">未启用</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="200">
+          <template #default="scope">
+            <el-button link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
+            <el-button link type="primary" @click="handleAdd(scope.row)">新增</el-button>
+            <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+
+    <!-- 新增/编辑部门对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="700px" append-to-body>
+      <el-form ref="deptFormRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="上级部门" prop="parentId">
+          <el-tree-select
+            v-model="form.parentId"
+            :data="deptOptions"
+            :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
+            value-key="deptId"
+            placeholder="请选择"
+            check-strictly
+            :render-after-expand="false"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="部门名称" prop="deptName">
+          <el-input v-model="form.deptName" placeholder="请输入部门名称" />
+        </el-form-item>
+        <el-form-item label="年度预算" prop="annualBudget">
+          <el-input v-model="form.annualBudget" placeholder="请输入年度预算" />
+        </el-form-item>
+        <el-form-item label="月采限额" prop="monthlyQuota">
+          <el-input v-model="form.monthlyQuota" placeholder="请输入月采限额" />
+        </el-form-item>
+        <el-form-item label="已用预算" prop="budgetUsed">
+          <el-input v-model="form.budgetUsed" placeholder="请输入已用预算" />
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-radio-group v-model="form.status">
+            <el-radio value="0">启用</el-radio>
+            <el-radio value="1">未启用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="是否绑定">
+          <el-switch v-model="form.isBound" :active-value="'0'" :inactive-value="'1'" />
+        </el-form-item>
+        <el-form-item v-if="form.isBound === '0'" label="绑定地址" prop="shopAddressId">
+          <el-select v-model="form.shopAddressId" placeholder="请选择" style="width: 100%">
+            <el-option label="地址1" value="address1" />
+            <el-option label="地址2" value="address2" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="OrgStructure" lang="ts">
+import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from '@/api/system/dept';
+import { DeptForm, DeptQuery, DeptVO } from '@/api/system/dept/types';
+
+interface DeptOptionsType {
+  deptId: number | string;
+  deptName: string;
+  children: DeptOptionsType[];
+}
+
+// 接收父组件传递的props
+const props = defineProps<{
+  customerId?: string | number;
+  customerNo?: string;
+  customerName?: string;
+}>();
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const deptList = ref<DeptVO[]>([]);
+const deptOptions = ref<DeptOptionsType[]>([]);
+const loading = ref(false);
+const buttonLoading = ref(false);
+
+const deptFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: DeptForm = {
+  deptId: undefined,
+  parentId: undefined,
+  deptName: '',
+  annualBudget: undefined,
+  monthlyQuota: undefined,
+  budgetUsed: undefined,
+  isBound: '1',
+  shopAddressId: undefined,
+  status: '0',
+  orderNum: -1,
+  customerId: undefined
+};
+
+const form = ref<DeptForm>({ ...initFormData });
+
+const rules = {
+  parentId: [{ required: false, message: '请选择上级部门', trigger: 'change' }],
+  deptName: [{ required: true, message: '请输入部门名称', trigger: 'blur' }],
+  annualBudget: [{ required: true, message: '请输入年度预算', trigger: 'blur' }],
+  monthlyQuota: [{ required: true, message: '请输入月采限额', trigger: 'blur' }],
+  budgetUsed: [{ required: true, message: '请输入已用预算', trigger: 'blur' }],
+  shopAddressId: [{ required: true, message: '请选择绑定地址', trigger: 'change' }]
+};
+
+/** 查询部门列表 */
+const getList = async () => {
+  loading.value = true;
+  const queryParams = {
+    customerId: props.customerId
+  };
+  try {
+    const res = await listDept(queryParams);
+    deptList.value = proxy?.handleTree(res.data, 'deptId') || [];
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 查询部门下拉树结构 */
+const getTreeSelect = async () => {
+  const queryParams = {
+    customerId: props.customerId
+  };
+  const res = await listDept(queryParams);
+  deptOptions.value = proxy?.handleTree(res.data, 'deptId') || [];
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  deptFormRef.value?.resetFields();
+};
+
+/** 新增按钮操作 */
+const handleAdd = async (row?: DeptVO) => {
+  reset();
+  await getTreeSelect();
+  if (row && row.deptId) {
+    form.value.parentId = row.deptId;
+  }
+  // 设置客户ID
+  form.value.customerId = props.customerId;
+  dialog.visible = true;
+  dialog.title = '新增部门';
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row: DeptVO) => {
+  reset();
+  await getTreeSelect();
+  const res = await getDept(row.deptId);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '修改部门';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  deptFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        // 确保提交时包含客户ID
+        const submitData = {
+          ...form.value,
+          customerId: props.customerId
+        };
+        if (form.value.deptId) {
+          await updateDept(submitData);
+          proxy?.$modal.msgSuccess('修改成功');
+        } else {
+          await addDept(submitData);
+          proxy?.$modal.msgSuccess('新增成功');
+        }
+        dialog.visible = false;
+        await getList();
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row: DeptVO) => {
+  try {
+    await proxy?.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的部门?');
+    await delDept(row.deptId);
+    await getList();
+    proxy?.$modal.msgSuccess('删除成功');
+  } catch {}
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 298 - 2
src/views/customer/customerFile/customerInfo/overview/orgStructure.vue

@@ -6,19 +6,315 @@
           <el-col :span="22">
             <span>企业组织结构信息列表</span>
           </el-col>
+          <el-col :span="2" style="text-align: right">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd">新增部门</el-button>
+          </el-col>
         </el-row>
       </template>
+
+      <el-table v-loading="loading" :data="customerDeptList" row-key="id" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
+        <el-table-column prop="deptName" label="部门名称" width="200" />
+        <el-table-column prop="yearlyBudget" label="年度预算" align="center" />
+        <el-table-column prop="monthLimit" label="月采限额" align="center" />
+        <el-table-column prop="usedBudget" label="已用预算" align="center" />
+        <el-table-column prop="bindAddress" label="绑定地址" align="center" />
+        <el-table-column label="状态" align="center">
+          <template #default="scope">
+            <el-tag v-if="scope.row.status === '0'" type="success">启用</el-tag>
+            <el-tag v-else type="info">未启用</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="200">
+          <template #default="scope">
+            <el-button link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
+            <el-button link type="primary" @click="handleAdd(scope.row)">新增</el-button>
+            <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
     </el-card>
+
+    <!-- 新增/编辑部门对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="700px" append-to-body>
+      <el-form ref="customerDeptFormRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="上级部门" prop="parentId">
+          <el-tree-select
+            v-model="form.parentId"
+            :data="customerDeptOptions"
+            :props="{ value: 'id', label: 'deptName', children: 'children' }"
+            value-key="id"
+            placeholder="请选择"
+            check-strictly
+            :render-after-expand="false"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="部门名称" prop="deptName">
+          <el-input v-model="form.deptName" placeholder="请输入部门名称" />
+        </el-form-item>
+        <el-form-item label="年度预算" prop="yearlyBudget">
+          <el-input v-model="form.yearlyBudget" placeholder="请输入年度预算" />
+        </el-form-item>
+        <el-form-item label="月采限额" prop="monthLimit">
+          <el-input v-model="form.monthLimit" placeholder="请输入月采限额" />
+        </el-form-item>
+        <el-form-item label="已用预算" prop="usedBudget">
+          <el-input v-model="form.usedBudget" placeholder="请输入已用预算" />
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-radio-group v-model="form.status">
+            <el-radio value="0">启用</el-radio>
+            <el-radio value="1">未启用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="是否绑定">
+          <el-switch v-model="form.bindStatus" :active-value="'0'" :inactive-value="'1'" />
+        </el-form-item>
+        <el-form-item v-if="form.bindStatus === '0'" label="绑定地址" prop="bindAddress">
+          <el-select v-model="form.bindAddress" placeholder="请选择" style="width: 100%">
+            <el-option label="地址1" value="address1" />
+            <el-option label="地址2" value="address2" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
-<script setup name="OrgStructure" lang="ts">
+<script setup name="CustomerDept" lang="ts">
+import { listCustomerDept, getCustomerDept, delCustomerDept, addCustomerDept, updateCustomerDept } from '@/api/customer/customerFile/customerDept';
+import { CustomerDeptVO, CustomerDeptQuery, CustomerDeptForm } from '@/api/customer/customerFile/customerDept/types';
+
+type CustomerDeptOption = {
+  id: number;
+  deptName: string;
+  children?: CustomerDeptOption[];
+};
+
 // 接收父组件传递的props
 const props = defineProps<{
-  customerId?: string;
+  customerId?: string | number;
   customerNo?: string;
   customerName?: string;
 }>();
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
+
+const customerDeptList = ref<CustomerDeptVO[]>([]);
+const customerDeptOptions = ref<CustomerDeptOption[]>([]);
+const buttonLoading = ref(false);
+const showSearch = ref(true);
+const isExpandAll = ref(true);
+const loading = ref(false);
+
+const queryFormRef = ref<ElFormInstance>();
+const customerDeptFormRef = ref<ElFormInstance>();
+const customerDeptTableRef = ref<ElTableInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: CustomerDeptForm = {
+  id: undefined,
+  deptNo: undefined,
+  deptName: undefined,
+  parentId: undefined,
+  ancestors: undefined,
+  customerId: undefined,
+  departmentLevel: undefined,
+  yearlyBudget: undefined,
+  usedBudget: undefined,
+  monthLimit: undefined,
+  monthUsedBudget: undefined,
+  bindStatus: undefined,
+  bindAddress: undefined,
+  deptManage: undefined,
+  isLimit: undefined,
+  selectYear: undefined,
+  expenseType: undefined,
+  residueYearlyBudget: undefined,
+  recharge: undefined,
+  status: undefined,
+  remark: undefined
+};
+
+const data = reactive<PageData<CustomerDeptForm, CustomerDeptQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    deptName: undefined,
+    parentId: undefined,
+    ancestors: undefined,
+    customerId: undefined,
+    departmentLevel: undefined,
+    yearlyBudget: undefined,
+    usedBudget: undefined,
+    monthLimit: undefined,
+    monthUsedBudget: undefined,
+    bindStatus: undefined,
+    bindAddress: undefined,
+    deptManage: undefined,
+    isLimit: undefined,
+    selectYear: undefined,
+    expenseType: undefined,
+    residueYearlyBudget: undefined,
+    recharge: undefined,
+    status: undefined,
+    platformCode: undefined,
+    params: {}
+  },
+  rules: {
+    deptName: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
+    customerId: [{ required: true, message: '客户编号不能为空', trigger: 'blur' }],
+    yearlyBudget: [{ required: true, message: '年度预算不能为空', trigger: 'blur' }],
+    usedBudget: [{ required: true, message: '已使用预算不能为空', trigger: 'blur' }],
+    monthLimit: [{ required: true, message: '月度限额不能为空', trigger: 'blur' }],
+    bindStatus: [{ required: true, message: '绑定状态不能为空', trigger: 'change' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询客户部门信息列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    queryParams.value.customerId = props.customerId;
+    const res = await listCustomerDept(queryParams.value);
+    // 如果返回的数据是数组,直接使用;否则尝试构建树形结构
+    if (Array.isArray(res.data)) {
+      const treeData = proxy?.handleTree<CustomerDeptVO>(res.data, 'id', 'parentId');
+      customerDeptList.value = treeData || res.data;
+    } else {
+      customerDeptList.value = [];
+    }
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 查询客户部门信息下拉树结构 */
+const getTreeselect = async () => {
+  const query: CustomerDeptQuery = {
+    customerId: props.customerId
+  };
+  const res = await listCustomerDept(query);
+  customerDeptOptions.value = proxy?.handleTree<CustomerDeptOption>(res.data, 'id', 'parentId') || [];
+};
+
+// 取消按钮
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+// 表单重置
+const reset = () => {
+  form.value = { ...initFormData };
+  customerDeptFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 新增按钮操作 */
+const handleAdd = async (row?: CustomerDeptVO) => {
+  reset();
+  await getTreeselect();
+  if (row != null && row.id) {
+    form.value.parentId = row.id;
+  }
+  // 设置客户ID
+  form.value.customerId = props.customerId;
+  form.value.status = '0';
+  form.value.bindStatus = '1';
+  dialog.visible = true;
+  dialog.title = '新增部门';
+};
+
+/** 展开/折叠操作 */
+const handleToggleExpandAll = () => {
+  isExpandAll.value = !isExpandAll.value;
+  toggleExpandAll(customerDeptList.value, isExpandAll.value);
+};
+
+/** 展开/折叠操作 */
+const toggleExpandAll = (data: CustomerDeptVO[], status: boolean) => {
+  data.forEach((item) => {
+    customerDeptTableRef.value?.toggleRowExpansion(item, status);
+    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
+  });
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row: CustomerDeptVO) => {
+  reset();
+  await getTreeselect();
+  const res = await getCustomerDept(row.id);
+  Object.assign(form.value, res.data);
+  // 修正数据:如果 parentId 等于自己的 id,说明是顶级部门,将 parentId 设为 0
+  if (form.value.parentId === form.value.id) {
+    form.value.parentId = 0;
+  }
+  dialog.visible = true;
+  dialog.title = '修改部门';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  customerDeptFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        // 确保提交时包含客户ID
+        const submitData = {
+          ...form.value,
+          customerId: props.customerId
+        };
+        if (form.value.id) {
+          await updateCustomerDept(submitData);
+          proxy?.$modal.msgSuccess('修改成功');
+        } else {
+          await addCustomerDept(submitData);
+          proxy?.$modal.msgSuccess('新增成功');
+        }
+        dialog.visible = false;
+        await getList();
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row: CustomerDeptVO) => {
+  try {
+    await proxy?.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的部门?');
+    await delCustomerDept(row.id);
+    await getList();
+    proxy?.$modal.msgSuccess('删除成功');
+  } catch {}
+};
+
+onMounted(() => {
+  getList();
+});
 </script>

+ 264 - 0
src/views/customer/customerFile/groupCustomer/index.vue

@@ -0,0 +1,264 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="22"><span>集团客户信息列表</span> </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['customer:groupCustomer:add']">新增</el-button>
+          </el-col>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="groupCustomerList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="企业ID" align="center" prop="topCustomerNo" />
+        <el-table-column label="企业名称" align="center" prop="topCustomerName" />
+        <el-table-column label="行业" align="center" prop="industryCategory" />
+        <el-table-column label="到账" align="center" prop="accountPeriod" />
+        <el-table-column label="客户等级" align="center" prop="customerLevel">
+          <template #default="scope">
+            <dict-tag :options="customer_level" :value="scope.row.customerLevel" />
+          </template>
+        </el-table-column>
+        <el-table-column label="信用额" align="center" prop="creditAmount">
+          <template #default="scope">
+            <span>{{ scope.row.creditAmount || '0.00' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="剩余额度" align="center" prop="remainingQuota">
+          <template #default="scope">
+            <span>{{ scope.row.remainingQuota || '0.00' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="应收账款" align="center" prop="accountsReceivable">
+          <template #default="scope">
+            <span>{{ scope.row.accountsReceivable || '0.00' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button link type="primary" icon="Edit" @click="handleDetail(scope.row)" v-hasPermi="['customer:groupCustomer:edit']">详情</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改集团客户关系对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body>
+      <el-form ref="groupCustomerFormRef" :model="form" :rules="rules" label-width="90px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="集团名称" prop="groupCustomerId">
+              <el-select v-model="form.groupCustomerId" placeholder="请选择" clearable filterable style="width: 100%">
+                <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="3">
+            <div style="line-height: 32px; text-align: right; padding-right: 12px; color: #606266">
+              <span>关联客户</span>
+            </div>
+          </el-col>
+          <el-col :span="21">
+            <div style="background-color: #f5f7fa; padding: 20px 16px; border-radius: 4px">
+              <el-row :gutter="20">
+                <el-col :span="8">
+                  <div style="margin-bottom: 8px; color: #606266; font-size: 14px">上级客户</div>
+                  <el-select v-model="form.topCustomerId" placeholder="请选择" clearable filterable style="width: 100%">
+                    <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
+                  </el-select>
+                </el-col>
+                <el-col :span="8">
+                  <div style="margin-bottom: 8px; color: #606266; font-size: 14px">客户名称</div>
+                  <el-select v-model="form.customerId" placeholder="请选择" clearable filterable style="width: 100%">
+                    <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
+                  </el-select>
+                </el-col>
+                <el-col :span="8">
+                  <div style="margin-bottom: 8px; color: #606266; font-size: 14px">客户等级</div>
+                  <el-select v-model="form.customerLevel" placeholder="请选择" clearable style="width: 100%">
+                    <el-option v-for="dict in customer_level" :key="dict.value" :label="dict.label" :value="dict.value" />
+                  </el-select>
+                </el-col>
+              </el-row>
+            </div>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 认</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="GroupCustomer" lang="ts">
+import {
+  listGroupCustomer,
+  getGroupCustomer,
+  delGroupCustomer,
+  addGroupCustomer,
+  updateGroupCustomer
+} from '@/api/customer/customerFile/groupCustomer';
+import { listCustomerInfo, getCustomerInfo } from '@/api/customer/customerFile/customerInfo';
+import { CustomerInfoVO, CustomerInfoQuery, CustomerInfoForm } from '@/api/customer/customerFile/customerInfo/types';
+import { GroupCustomerVO, GroupCustomerQuery, GroupCustomerForm } from '@/api/customer/customerFile/groupCustomer/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { customer_level } = toRefs<any>(proxy?.useDict('customer_level'));
+const groupCustomerList = ref<GroupCustomerVO[]>([]);
+const customerList = ref<CustomerInfoVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const groupCustomerFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: GroupCustomerForm = {
+  id: undefined,
+  groupCustomerId: undefined,
+  customerId: undefined,
+  customerLevel: undefined,
+  topCustomerId: undefined,
+  remark: undefined
+};
+const data = reactive<PageData<GroupCustomerForm, GroupCustomerQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    groupCustomerId: undefined,
+    customerId: undefined,
+    customerLevel: undefined,
+    topCustomerId: undefined,
+    platformCode: undefined,
+    params: {}
+  },
+  rules: {}
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询集团客户关系列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listGroupCustomer(queryParams.value);
+  groupCustomerList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  groupCustomerFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: GroupCustomerVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 加载客户列表 */
+const loadCustomerList = async () => {
+  const res = await listCustomerInfo({ pageNum: 1, pageSize: 10000 });
+  customerList.value = res.rows;
+};
+
+/** 新增按钮操作 */
+const handleAdd = async () => {
+  reset();
+  await loadCustomerList();
+  dialog.visible = true;
+  dialog.title = '新增集团客户';
+};
+
+/** 修改按钮操作 */
+const handleDetail = async (row?: GroupCustomerVO) => {
+  reset();
+  await loadCustomerList();
+  const _id = row?.id || ids.value[0];
+  const res = await getGroupCustomer(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '查看集团客户';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  groupCustomerFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateGroupCustomer(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addGroupCustomer(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: GroupCustomerVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除集团客户关系编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delGroupCustomer(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'customer/groupCustomer/export',
+    {
+      ...queryParams.value
+    },
+    `groupCustomer_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>