ソースを参照

Merge branch 'dev' into xinyan

xinyan 7 ヶ月 前
コミット
856312d11b

+ 2 - 2
.env

@@ -1,8 +1,8 @@
 # port 端口号
 VITE_PORT=8082
-VITE_API_URL='http://dvadmin3api.django.icu:8001'
+# VITE_API_URL='http://dvadmin3api.django.icu:8001'
 # open 运行 npm run dev 时自动打开浏览器
 VITE_OPEN=false
 
 # public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
-VITE_PUBLIC_PATH=/
+VITE_PUBLIC_PATH=/web

+ 2 - 2
.env.development

@@ -3,9 +3,9 @@ ENV='development'
 
 # 本地环境接口地址
 # VITE_API_URL = 'http://127.0.0.1:8000'
-VITE_API_URL='http://192.168.1.225:9090/'
+ VITE_API_URL='http://192.168.1.225:9090/'
 # VITE_API_URL = 'http://192.168.1.22:8080/'
-# VITE_API_URL = 'http://amzads.zositechc.cn'
+# VITE_API_URL='http://asset.zositechc.cn'
 
 # 是否启用按钮权限
 VITE_PM_ENABLED=true

+ 0 - 58
components.d.ts

@@ -15,61 +15,6 @@ declare module 'vue' {
     DeptFormat: typeof import('./src/components/dept-format/index.vue')['default']
     DvaSelect: typeof import('./src/components/dvaSelect/index.vue')['default']
     Editor: typeof import('./src/components/editor/index.vue')['default']
-    ElAlert: typeof import('element-plus/es')['ElAlert']
-    ElAside: typeof import('element-plus/es')['ElAside']
-    ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
-    ElBacktop: typeof import('element-plus/es')['ElBacktop']
-    ElBadge: typeof import('element-plus/es')['ElBadge']
-    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
-    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
-    ElButton: typeof import('element-plus/es')['ElButton']
-    ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
-    ElCard: typeof import('element-plus/es')['ElCard']
-    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
-    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
-    ElCol: typeof import('element-plus/es')['ElCol']
-    ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
-    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
-    ElContainer: typeof import('element-plus/es')['ElContainer']
-    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
-    ElDialog: typeof import('element-plus/es')['ElDialog']
-    ElDivider: typeof import('element-plus/es')['ElDivider']
-    ElDrawer: typeof import('element-plus/es')['ElDrawer']
-    ElDropdown: typeof import('element-plus/es')['ElDropdown']
-    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
-    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
-    ElEmpty: typeof import('element-plus/es')['ElEmpty']
-    ElForm: typeof import('element-plus/es')['ElForm']
-    ElFormItem: typeof import('element-plus/es')['ElFormItem']
-    ElHeader: typeof import('element-plus/es')['ElHeader']
-    ElIcon: typeof import('element-plus/es')['ElIcon']
-    ElImage: typeof import('element-plus/es')['ElImage']
-    ElInput: typeof import('element-plus/es')['ElInput']
-    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
-    ElMain: typeof import('element-plus/es')['ElMain']
-    ElMenu: typeof import('element-plus/es')['ElMenu']
-    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
-    ElOption: typeof import('element-plus/es')['ElOption']
-    ElPagination: typeof import('element-plus/es')['ElPagination']
-    ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
-    ElPopover: typeof import('element-plus/es')['ElPopover']
-    ElRadio: typeof import('element-plus/es')['ElRadio']
-    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
-    ElRow: typeof import('element-plus/es')['ElRow']
-    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
-    ElSelect: typeof import('element-plus/es')['ElSelect']
-    ElSelectV2: typeof import('element-plus/es')['ElSelectV2']
-    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
-    ElSwitch: typeof import('element-plus/es')['ElSwitch']
-    ElTable: typeof import('element-plus/es')['ElTable']
-    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
-    ElTabPane: typeof import('element-plus/es')['ElTabPane']
-    ElTabs: typeof import('element-plus/es')['ElTabs']
-    ElTag: typeof import('element-plus/es')['ElTag']
-    ElText: typeof import('element-plus/es')['ElText']
-    ElTooltip: typeof import('element-plus/es')['ElTooltip']
-    ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
-    ElUpload: typeof import('element-plus/es')['ElUpload']
     ForeignKey: typeof import('./src/components/foreignKey/index.vue')['default']
     IconSelector: typeof import('./src/components/iconSelector/index.vue')['default']
     ImportExcel: typeof import('./src/components/importExcel/index.vue')['default']
@@ -82,7 +27,4 @@ declare module 'vue' {
     Table: typeof import('./src/components/table/index.vue')['default']
     TableSelector: typeof import('./src/components/tableSelector/index.vue')['default']
   }
-  export interface ComponentCustomProperties {
-    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
-  }
 }

+ 105 - 92
src/layout/navBars/breadcrumb/breadcrumb.vue

@@ -1,29 +1,35 @@
 <template>
-	<div v-if="isShowBreadcrumb" class="layout-navbars-breadcrumb">
-		<SvgIcon
-			class="layout-navbars-breadcrumb-icon"
-			:name="themeConfig.isCollapse ? 'ele-Expand' : 'ele-Fold'"
-			:size="16"
-			@click="onThemeConfigChange"
-		/>
-		<el-breadcrumb class="layout-navbars-breadcrumb-hide">
-			<transition-group name="breadcrumb">
-				<el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="!v.meta.tagsViewName ? v.meta.title : v.meta.tagsViewName">
+  <div v-if="isShowBreadcrumb" class="layout-navbars-breadcrumb">
+    <SvgIcon
+        :name="themeConfig.isCollapse ? 'ele-Expand' : 'ele-Fold'"
+        :size="16"
+        class="layout-navbars-breadcrumb-icon"
+        @click="onThemeConfigChange"
+    />
+    <el-breadcrumb class="layout-navbars-breadcrumb-hide">
+      <transition-group name="breadcrumb">
+        <el-breadcrumb-item v-for="(v, k) in state.breadcrumbList"
+                            :key="!v.meta.tagsViewName ? v.meta.title : v.meta.tagsViewName">
 					<span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
-						<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />
+						<SvgIcon v-if="themeConfig.isBreadcrumbIcon" :name="v.meta.icon"
+                     class="layout-navbars-breadcrumb-iconfont"/>
 						<div v-if="!v.meta.tagsViewName">{{ $t(v.meta.title) }}</div>
 						<div v-else>{{ v.meta.tagsViewName }}</div>
 					</span>
-					<a v-else @click.prevent="onBreadcrumbClick(v)">
-						<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />{{ $t(v.meta.title) }}
-					</a>
-				</el-breadcrumb-item>
-			</transition-group>
-		</el-breadcrumb>
-	</div>
+          <a v-else @click.prevent="onBreadcrumbClick(v)" class="flex">
+            <SvgIcon v-if="themeConfig.isBreadcrumbIcon" :name="v.meta.icon"
+                     class="layout-navbars-breadcrumb-iconfont"/>
+            <span >
+              {{ $t(v.meta.title) }}
+            </span>
+          </a>
+        </el-breadcrumb-item>
+      </transition-group>
+    </el-breadcrumb>
+  </div>
 </template>
 
-<script setup lang="ts" name="layoutBreadcrumb">
+<script lang="ts" name="layoutBreadcrumb" setup>
 import { reactive, computed, onMounted } from 'vue';
 import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 import { Local } from '/@/utils/storage';
@@ -40,107 +46,114 @@ const { routesList } = storeToRefs(stores);
 const route = useRoute();
 const router = useRouter();
 const state = reactive<BreadcrumbState>({
-	breadcrumbList: [],
-	routeSplit: [],
-	routeSplitFirst: '',
-	routeSplitIndex: 1,
+  breadcrumbList: [],
+  routeSplit: [],
+  routeSplitFirst: '',
+  routeSplitIndex: 1
 });
 
 // 动态设置经典、横向布局不显示
 const isShowBreadcrumb = computed(() => {
-	initRouteSplit(route.path);
-	const { layout, isBreadcrumb } = themeConfig.value;
-	if (layout === 'classic' || layout === 'transverse') return false;
-	else return isBreadcrumb ? true : false;
+  initRouteSplit(route.path);
+  const { layout, isBreadcrumb } = themeConfig.value;
+  if (layout === 'classic' || layout === 'transverse') return false;
+  else return isBreadcrumb ? true : false;
 });
 // 面包屑点击时
 const onBreadcrumbClick = (v: RouteItem) => {
-	const { redirect, path } = v;
-	if (redirect) router.push(redirect);
-	else router.push(path);
+  const { redirect, path } = v;
+  if (redirect) router.push(redirect);
+  else router.push(path);
 };
 // 展开/收起左侧菜单点击
 const onThemeConfigChange = () => {
-	themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
-	setLocalThemeConfig();
+  themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
+  setLocalThemeConfig();
 };
 // 存储布局配置
 const setLocalThemeConfig = () => {
-	Local.remove('themeConfig');
-	Local.set('themeConfig', themeConfig.value);
+  Local.remove('themeConfig');
+  Local.set('themeConfig', themeConfig.value);
 };
 // 处理面包屑数据
 const getBreadcrumbList = (arr: RouteItems) => {
-	arr.forEach((item: RouteItem) => {
-		state.routeSplit.forEach((v: string, k: number, arrs: string[]) => {
-			if (state.routeSplitFirst === item.path) {
-				state.routeSplitFirst += `/${arrs[state.routeSplitIndex]}`;
-				state.breadcrumbList.push(item);
-				state.routeSplitIndex++;
-				if (item.children) getBreadcrumbList(item.children);
-			}
-		});
-	});
+  arr.forEach((item: RouteItem) => {
+    state.routeSplit.forEach((v: string, k: number, arrs: string[]) => {
+      if (state.routeSplitFirst === item.path) {
+        state.routeSplitFirst += `/${ arrs[state.routeSplitIndex] }`;
+        state.breadcrumbList.push(item);
+        state.routeSplitIndex++;
+        if (item.children) getBreadcrumbList(item.children);
+      }
+    });
+  });
 };
 // 当前路由字符串切割成数组,并删除第一项空内容
 const initRouteSplit = (path: string) => {
-	if (!themeConfig.value.isBreadcrumb) return false;
-	state.breadcrumbList = [routesList.value[0]];
-	state.routeSplit = path.split('/');
-	state.routeSplit.shift();
-	state.routeSplitFirst = `/${state.routeSplit[0]}`;
-	state.routeSplitIndex = 1;
-	getBreadcrumbList(routesList.value);
-	if (route.name === 'home' || (route.name === 'notFound' && state.breadcrumbList.length > 1)) state.breadcrumbList.shift();
-	if (state.breadcrumbList.length > 0)
-		state.breadcrumbList[state.breadcrumbList.length - 1].meta.tagsViewName = other.setTagsViewNameI18n(<RouteToFrom>route);
+  if (!themeConfig.value.isBreadcrumb) return false;
+  state.breadcrumbList = [ routesList.value[0] ];
+  state.routeSplit = path.split('/');
+  state.routeSplit.shift();
+  state.routeSplitFirst = `/${ state.routeSplit[0] }`;
+  state.routeSplitIndex = 1;
+  getBreadcrumbList(routesList.value);
+  if (route.name === 'home' || (route.name === 'notFound' && state.breadcrumbList.length > 1)) state.breadcrumbList.shift();
+  if (state.breadcrumbList.length > 0)
+    state.breadcrumbList[state.breadcrumbList.length - 1].meta.tagsViewName = other.setTagsViewNameI18n(<RouteToFrom>route);
 };
 // 页面加载时
 onMounted(() => {
-	initRouteSplit(route.path);
+  initRouteSplit(route.path);
 });
 // 路由更新时
 onBeforeRouteUpdate((to) => {
-	initRouteSplit(to.path);
+  initRouteSplit(to.path);
 });
 </script>
 
-<style scoped lang="scss">
+<style lang="scss" scoped>
 .layout-navbars-breadcrumb {
-	flex: 1;
-	height: inherit;
-	display: flex;
-	align-items: center;
-	.layout-navbars-breadcrumb-icon {
-		cursor: pointer;
-		font-size: 18px;
-		color: var(--next-bg-topBarColor);
-		height: 100%;
-		width: 40px;
-		opacity: 0.8;
-		&:hover {
-			opacity: 1;
-		}
-	}
-	.layout-navbars-breadcrumb-span {
-		display: flex;
-		opacity: 0.7;
-		color: var(--next-bg-topBarColor);
-	}
-	.layout-navbars-breadcrumb-iconfont {
-		font-size: 14px;
-		margin-right: 5px;
-	}
-	:deep(.el-breadcrumb__separator) {
-		opacity: 0.7;
-		color: var(--next-bg-topBarColor);
-	}
-	:deep(.el-breadcrumb__inner a, .el-breadcrumb__inner.is-link) {
-		font-weight: unset !important;
-		color: var(--next-bg-topBarColor);
-		&:hover {
-			color: var(--el-color-primary) !important;
-		}
-	}
+  flex: 1;
+  height: inherit;
+  display: flex;
+  align-items: center;
+
+  .layout-navbars-breadcrumb-icon {
+    cursor: pointer;
+    font-size: 18px;
+    color: var(--next-bg-topBarColor);
+    height: 100%;
+    width: 40px;
+    opacity: 0.8;
+
+    &:hover {
+      opacity: 1;
+    }
+  }
+
+  .layout-navbars-breadcrumb-span {
+    display: flex;
+    opacity: 0.7;
+    color: var(--next-bg-topBarColor);
+  }
+
+  .layout-navbars-breadcrumb-iconfont {
+    font-size: 14px;
+    margin-right: 5px;
+  }
+
+  :deep(.el-breadcrumb__separator) {
+    opacity: 0.7;
+    color: var(--next-bg-topBarColor);
+  }
+
+  :deep(.el-breadcrumb__inner a, .el-breadcrumb__inner.is-link) {
+    font-weight: unset !important;
+    color: var(--next-bg-topBarColor);
+
+    &:hover {
+      color: var(--el-color-primary) !important;
+    }
+  }
 }
 </style>

+ 2 - 2
src/stores/themeConfig.ts

@@ -68,7 +68,7 @@ export const useThemeConfig = defineStore('themeConfig', {
 			// 是否开启菜单手风琴效果
 			isUniqueOpened: true,
 			// 是否开启固定 Header
-			isFixedHeader: false,
+			isFixedHeader: true,
 			// 初始化变量,用于更新菜单 el-scrollbar 的高度,请勿删除
 			isFixedHeaderChange: false,
 			// 是否开启经典布局分割菜单(仅经典布局生效)
@@ -88,7 +88,7 @@ export const useThemeConfig = defineStore('themeConfig', {
 			// 是否开启 Breadcrumb,强制经典、横向布局不显示
 			isBreadcrumb: true,
 			// 是否开启 Tagsview
-			isTagsview: true,
+			isTagsview: false,
 			// 是否开启 Breadcrumb 图标
 			isBreadcrumbIcon: true,
 			// 是否开启 Tagsview 图标

+ 8 - 39
src/views/company-information/api.ts

@@ -13,56 +13,25 @@ export function getCardData(query: any) {
 
 export function getTableData(query: any) {
   return request({
-    url: apiPrefix,
+    url: apiPrefix + 'shop/',
     method: 'GET',
     params: query,
   });
 }
 
-export function getPlatformDetailOverview(query: any) {
+export function getCompanyOverview(query: any) {
   return request({
-    url: apiPrefix + 'platform/',
+    url: apiPrefix + `${query.companyId}/`,
     method: 'GET',
-    params: query,
-  });
-}
-
-export function getShopDetailOverview(query: any) {
-  return request({
-    url: apiPrefix + 'detail/',
-    method: 'GET',
-    params: query,
   });
 }
 
-export function getCurrentInfo(query: any) {
+export function updateShopDetail(query: any) {
   return request({
-    url: apiPrefix + 'current/',
-    method: 'GET',
-    params: query,
-  });
-}
-
-export function getHistoryInfo(query: any) {
-  return request({
-    url: apiPrefix + 'past/',
-    method: 'GET',
-    params: query,
-  });
-}
-
-export function getComputerInfo(query: any) {
-  return request({
-    url: apiPrefix + 'computer/',
-    method: 'GET',
-    params: query,
-  });
-}
-
-export function getShopSelect() {
-  return request({
-    url: apiPrefix + 'box/',
-    method: 'GET',
+    url: apiPrefix + `${query.id}/`,
+    method: 'POST',
+    params: { partial: query.partial },
+    data: query.formData,
   });
 }
 

+ 0 - 1
src/views/company-information/components/CompanyCreate.vue

@@ -146,7 +146,6 @@ const resetForm = (formEl: FormInstance | undefined) => {
         <div class="flex gap-2.5 items-start">
           <el-form-item
               :prop="'contacts.' + index + '.phone'"
-              :rules="rules.contacts.phone"
           >
             <el-input
                 v-model="contact.phone"

+ 103 - 20
src/views/company-information/components/CompanyDetail.vue

@@ -5,17 +5,19 @@
  * @Author: Cheney
  */
 
-import { Link, Picture as IconPicture } from '@element-plus/icons-vue';
-import { platformColumns } from '/@/views/shop-information/useColumns';
+import { Edit, Link, Picture as IconPicture } from '@element-plus/icons-vue';
+import { companyColumns } from '../useColumns';
 import { usePagination } from '/@/utils/usePagination';
 import { useTableData } from '/@/utils/useTableData';
-import * as api from '/@/views/shop-information/api';
+import * as api from '../api';
 import router from '/@/router';
+import { useResponse } from '/@/utils/useResponse';
+import EditDrawer from '../components/EditDrawer.vue';
 
 
 const route = useRoute();
-const companyId = route.query.platform;
-const platformOverview: any = ref([]);
+const companyId = route.query.id;
+const companyOverview: any = ref([]);
 const overviewLoading = ref();
 const { tableOptions, handlePageChange } = usePagination(fetchCompanyDetail);
 const platformTableData = ref([]);
@@ -44,67 +46,139 @@ const gridOptions: any = reactive({
     icon: 'vxe-icon-indicator roll',
     text: '正在拼命加载中...'
   },
-  columns: platformColumns,
+  columns: companyColumns,
   data: platformTableData
 });
 
+const isOpen = ref(false);
+
+onBeforeMount(() => {
+  fetchCompanyOverview();
+  fetchCompanyDetail();
+});
+
 async function fetchCompanyDetail() {
   const query = {
-    companyId,
+    id: companyId,
     page: gridOptions.pagerConfig.page,
     limit: gridOptions.pagerConfig.limit
   };
   await useTableData(api.getTableData, query, gridOptions);
 }
 
+async function fetchCompanyOverview() {
+  const res = await useResponse({ companyId }, api.getCompanyOverview, overviewLoading);
+  companyOverview.value = res.data;
+}
+
 function handleNavigate(item: any) {
   router.push({
     path: '/shop/detail',
     query: { platformNumber: item.platformNumber }
   });
 }
+
+function drawerOpen() {
+  isOpen.value = true;
+}
+
+function handleRefresh() {
+  fetchCompanyOverview();
+}
+
+const contactGroups = computed(() => {
+  const contacts = companyOverview.value?.companyPhoneEmail || [];
+  if (!Array.isArray(contacts)) {
+    console.error('companyPhoneEmail is not an array:', contacts);
+    return [];
+  }
+
+  const groups = [];
+
+  if (contacts.length > 0) {
+    groups.push(contacts.slice(0, 2));
+  }
+
+  for (let i = 2; i < contacts.length; i += 3) {
+    groups.push(contacts.slice(i, i + 3));
+  }
+
+  return groups;
+});
 </script>
 
 <template>
   <div class="p-2.5">
-    <el-card v-loading="overviewLoading" body-class="flex items-center" shadow="hover" style="border: none">
+    <el-card v-loading="overviewLoading" body-class="flex items-center gap-10" shadow="hover" style="border: none">
       <el-image :src="`/src/assets/platformImg/${ companyId }.png`" class="mr-7 rounded-2xl" fit="contain"
                 style="height: 120px; width: 120px;">
         <template #error>
           <div class="mr-3.5 flex items-center justify-center text-5xl"
-               style="height: 130px; width: 130px; background-color: #f5f5f5">
+               style="height: 100%; width: 100%; background-color: #f5f5f5">
             <el-icon>
               <icon-picture/>
             </el-icon>
           </div>
         </template>
       </el-image>
-      <div class="text-lg">
-        <div class="font-semibold">
-          公司编号/名称:
-          <span class="font-medium italic ml-1.5" style="color: #64748b"> 
-            {{ platformOverview[0]?.platform ? platformOverview[0]?.platform : '--' }} 
+      <div class="text-lg whitespace-nowrap">
+        <div class="font-semibold relative">
+          <el-button :icon="Edit" class="absolute" link style="left: -20px; bottom: 2px;" type="warning"
+                     @click="drawerOpen"></el-button>
+          公司名称:
+          <span class="font-medium italic ml-1.5" style="color: #64748b;"> 
+            {{ companyOverview?.company ? companyOverview?.company : '--' }} 
           </span>
         </div>
         <div class="font-semibold">
-          公司:
+          公司英文名:
           <span class="font-medium italic ml-1.5" style="color: #64748b">
-            {{ platformOverview[0]?.countCompany ? platformOverview[0]?.countCompany : '--' }} 
+            {{ companyOverview?.companyEnglishName ? companyOverview?.companyEnglishName : '--' }} 
           </span>
         </div>
         <div class="font-semibold">
-          平台:
+          公司地址:
           <span class="font-medium italic ml-1.5" style="color: #64748b">
-            {{ platformOverview[0]?.countShop ? platformOverview[0]?.countShop : '--' }}
+            {{ companyOverview?.address ? companyOverview?.address : '--' }}
+          </span>
+        </div>
+      </div>
+      <div class="text-lg whitespace-nowrap">
+        <div class="font-semibold">
+          公司法人:
+          <span class="font-medium italic ml-1.5" style="color: #64748b"> 
+            {{ companyOverview?.juridicalPerson ? companyOverview?.juridicalPerson : '--' }} 
           </span>
         </div>
         <div class="font-semibold">
-          电脑:
+          VAT税号:
           <span class="font-medium italic ml-1.5" style="color: #64748b"> 
-            {{ platformOverview[0]?.countComputer ? platformOverview[0]?.countComputer : '--' }}
+            {{ companyOverview?.vatNumber ? companyOverview?.vatNumber : '--' }} 
+          </span>
+        </div>
+        <div class="font-semibold">
+          VAT税号公司:
+          <span class="font-medium italic ml-1.5" style="color: #64748b"> 
+            {{ companyOverview?.vatCompany ? companyOverview?.vatCompany : '--' }} 
           </span>
         </div>
       </div>
+      <div v-if="contactGroups.length > 0" class="flex flex-wrap whitespace-nowrap">
+        <div v-for="(group, groupIndex) in contactGroups" :key="groupIndex">
+          <div class="text-lg font-semibold">
+            <template v-if="groupIndex === 0">
+              公司联系方式:
+            </template>
+            <div v-for="(item, itemIndex) in group" :key="itemIndex"
+                 class="font-medium italic mr-3" style="color: #64748b">
+              {{ item.phone }} - {{ item.email }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <div v-else class="text-lg font-semibold">
+        暂无联系方式
+      </div>
     </el-card>
 
     <el-card body-style="padding-top: 10px" class="mt-2.5" shadow="hover" style="border: none">
@@ -128,6 +202,15 @@ function handleNavigate(item: any) {
         </template>
       </vxe-grid>
     </el-card>
+
+    <EditDrawer
+        v-if="isOpen"
+        v-model="isOpen"
+        :company="companyOverview.company"
+        :companyOverview
+        :formSelect
+        @refresh="handleRefresh"
+    />
   </div>
 </template>
 

+ 213 - 0
src/views/company-information/components/EditDrawer.vue

@@ -0,0 +1,213 @@
+<script lang="ts" setup>
+/**
+ * @Name: EditDrawer.vue
+ * @Description: 公司编辑
+ * @Author: Cheney
+ */
+
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '../api';
+import { Delete, Plus } from '@element-plus/icons-vue';
+import { cloneDeep } from 'lodash';
+
+
+const loading = ref(false);
+const isOpen = defineModel({ default: false });
+const { companyOverview, company } = defineProps<{
+  companyOverview: any;
+  company: any;
+}>();
+
+const emit = defineEmits([ 'refresh' ]);
+
+onBeforeMount(() => {
+  replaceCol();
+});
+
+interface ContactItem {
+  phone: string;
+  email: string;
+}
+
+interface RuleForm {
+  company: string;
+  companyEnglishName: string;
+  address: string;
+  juridicalPerson: string;
+  vatNumber: string;
+  vatCompany: string;
+  contacts: ContactItem[];
+}
+
+const ruleFormRef = ref<FormInstance>();
+const ruleForm = reactive<RuleForm>({
+  company: '',
+  companyEnglishName: '',
+  address: '',
+  juridicalPerson: '',
+  vatNumber: '',
+  vatCompany: '',
+  contacts: [
+    {
+      phone: '',
+      email: ''
+    }
+  ]
+});
+
+const rules = reactive<FormRules<RuleForm>>({
+  company: [
+    { required: true, message: 'Please input platform name', trigger: 'blur' }
+  ],
+  companyEnglishName: [
+    { required: true, message: 'Please input platform name', trigger: 'blur' }
+  ],
+  address: [
+    { required: true, message: 'Please input country name', trigger: 'blur' }
+  ],
+  juridicalPerson: [
+    { required: true, message: 'Please input platform name', trigger: 'blur' }
+  ],
+  vatNumber: [
+    { required: true, message: 'Please select line', trigger: 'change' }
+  ],
+  vatCompany: [
+    { required: true, message: 'Please select line', trigger: 'change' }
+  ],
+  contacts: {
+    phone: [ { required: true, message: '请输入电话号码', trigger: 'blur' } ],
+    email: [
+      { required: true, message: '请输入邮箱地址', trigger: 'blur' },
+      { type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' }
+    ]
+  }
+});
+
+const addContact = () => {
+  ruleForm.contacts.push({
+    phone: '',
+    email: ''
+  });
+};
+
+const removeContact = (item: ContactItem) => {
+  const index = ruleForm.contacts.indexOf(item);
+  if (index !== -1) {
+    ruleForm.contacts.splice(index, 1);
+  }
+};
+
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      const formData = {
+        ...ruleForm,
+        companyPhoneEmail: ruleForm.contacts.map(contact => ({
+          email: contact.email,
+          phone: contact.phone
+        }))
+      };
+      await useResponse({ id: companyOverview.id, partial: 1, formData }, api.updateShopDetail, loading);
+      isOpen.value = false;
+      ElMessage.success('编辑成功');
+      emit('refresh');
+    } else {
+      console.log('error submit!', fields);
+    }
+  });
+};
+
+const resetForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.resetFields();
+};
+
+function replaceCol() {
+  const result = Object.keys(ruleForm).reduce((acc, key) => {
+    if (key in companyOverview) {
+      acc[key] = companyOverview[key];
+    }
+    return acc;
+  }, {} as { [key: string]: any });
+
+  // 处理 contacts 字段
+  if (companyOverview.companyPhoneEmail) {
+    result.contacts = cloneDeep(companyOverview.companyPhoneEmail);  // 深拷贝
+  }
+
+  Object.assign(ruleForm, result);
+}
+</script>
+
+<template>
+  <el-drawer v-model="isOpen" :title="`公司编辑 - ${company}`" size="30%">
+    <el-form
+        ref="ruleFormRef"
+        :model="ruleForm"
+        :rules="rules"
+        class="mx-2.5 mt-2.5"
+        label-width="auto"
+        status-icon>
+      <el-form-item label="公司名称" prop="platformNumber">
+        <el-input v-model="ruleForm.company"/>
+      </el-form-item>
+      <el-form-item label="公司英文名" prop="platformName">
+        <el-input v-model="ruleForm.companyEnglishName"/>
+      </el-form-item>
+      <el-form-item label="公司地址" prop="country">
+        <el-input v-model="ruleForm.address"/>
+      </el-form-item>
+      <el-form-item label="公司法人" prop="brandName">
+        <el-input v-model="ruleForm.juridicalPerson"/>
+      </el-form-item>
+      <el-form-item label="VAT税号" prop="status">
+        <el-input v-model="ruleForm.vatNumber"/>
+      </el-form-item>
+      <el-form-item label="VAT税号公司" prop="platform">
+        <el-input v-model="ruleForm.vatCompany"/>
+      </el-form-item>
+
+      <el-form-item
+          v-for="(contact, index) in ruleForm.contacts"
+          :key="index"
+          :label="'联系方式' + (index + 1)"
+      >
+        <div class="flex gap-2.5 items-start">
+          <el-form-item
+              :prop="'contacts.' + index + '.phone'"
+          >
+            <el-input
+                v-model="contact.phone"
+                placeholder="请输入电话"
+            />
+          </el-form-item>
+          <el-form-item
+              :prop="'contacts.' + index + '.email'"
+              :rules="rules.contacts.email"
+          >
+            <el-input
+                v-model="contact.email"
+                placeholder="请输入邮箱"
+            />
+          </el-form-item>
+          <el-button :icon="Delete" type="danger" @click.prevent="removeContact(contact)">
+            删 除
+          </el-button>
+        </div>
+      </el-form-item>
+      <el-form-item>
+        <div class="flex flex-1 justify-center">
+          <el-button :icon="Plus" type="warning" @click="addContact">新增联系方式</el-button>
+          <el-button :loading="loading" type="primary" @click="submitForm(ruleFormRef)">确 定</el-button>
+          <el-button @click="resetForm(ruleFormRef)">重 置</el-button>
+        </div>
+      </el-form-item>
+    </el-form>
+  </el-drawer>
+</template>
+
+<style scoped>
+
+</style>

+ 6 - 5
src/views/company-information/index.vue

@@ -1,4 +1,5 @@
-<script lang="ts" setup>/**
+<script lang="ts" setup>
+/**
  * @Name: index.vue
  * @Description: 公司信息页面
  * @Author: Cheney
@@ -37,19 +38,19 @@ function handleRefresh() {
 </script>
 
 <template>
-  <div class="px-2.5">
-    <el-card class="my-2.5" shadow="never" style="border: none;">
+  <div class="flex flex-col p-5">
+    <el-card class="mb-5" style="border: none;">
       <div class="flex justify-between items-baseline">
         <div>
           <span class="font-bold text-xl">公司信息概览</span>
           <el-divider class=" text-3xl" direction="vertical"/>
         </div>
         <span>
-           <el-button :icon="Plus" plain type="primary" @click="addCompany">添 加</el-button>
+           <el-button :icon="Plus" type="primary" @click="addCompany">添 加</el-button>
         </span>
       </div>
     </el-card>
-    <el-card v-loading="loading" shadow="never" style="border: none; min-height: 692px;">
+    <el-card v-loading="loading" class="flex-grow" style="border: none;">
       <InfoCard></InfoCard>
     </el-card>
     <CompanyCreate v-model="isOpen" @refresh="handleRefresh"/>

+ 74 - 0
src/views/company-information/useColumns.tsx

@@ -0,0 +1,74 @@
+import { useCountryInfoStore } from '/@/stores/countryInfo';
+
+
+const countryInfoStore = useCountryInfoStore();
+
+export const companyColumns = [
+  { type: 'seq', width: 50, align: 'center', fixed: 'left' },
+  {
+    field: 'platform', title: '平台', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.platform ? row.platform : '--' }</span>;
+      }
+    }
+  },
+  {
+    field: 'platformNumber', title: '平台编号', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.platformNumber ? row.platformNumber : '--' }</span>;
+      }
+    }
+  },
+  {
+    field: 'platformName', title: '平台名称', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.platformName ? row.platformName : '--' }</span>;
+      }
+    }
+  },
+  {
+    field: 'status', title: '状态', width: 80, align: 'center',
+    slots: {
+      default({ row }: any) {
+        return (
+            <el-tag
+                class="font-medium"
+                type={ row.status === 1 ? 'success' : 'danger' }
+            >
+              { row.status === 1 ? '启用' : '暂停' }
+            </el-tag>
+        );
+      }
+    }
+  },
+  {
+    field: 'country', title: '国家', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        const country = countryInfoStore.countries.find(c => c.name === row.country);
+        const color = country ? country.color : '#3875F6';
+        return <el-tag effect="plain" round
+                       style={ { color: color, borderColor: color } }>{ row.country ? row.country : '--' }</el-tag>;
+      }
+    }
+  },
+  {
+    field: 'brandName', title: '品牌', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.brandName ? row.brandName : '--' }</span>;
+      }
+    }
+  },
+  {
+    field: 'line', title: '线路', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.line ? row.line : '--' }</span>;
+      }
+    }
+  },
+];

+ 6 - 0
src/views/shop-information/api.ts

@@ -65,6 +65,12 @@ export function getShopSelect() {
     method: 'GET',
   });
 }
+export function getCompanySelect() {
+  return request({
+    url: '/api/assets/company/box/',
+    method: 'GET',
+  });
+}
 
 export function updateShopDetail(body: any) {
   return request({

+ 18 - 3
src/views/shop-information/components/EditDrawer.vue

@@ -1,21 +1,22 @@
 <script lang="ts" setup>
 /**
  * @Name: EditDrawer.vue
- * @Description:
+ * @Description: 店铺编辑
  * @Author: Cheney
  */
 
-import type { FormInstance, FormRules } from 'element-plus';
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
 import { useResponse } from '/@/utils/useResponse';
 import * as api from '/@/views/shop-information/api';
 
 
 const loading = ref(false);
 const isOpen = defineModel({ default: false });
-const { gridOptions, platformNumber, formSelect } = defineProps<{
+const { gridOptions, platformNumber, formSelect, companySelect } = defineProps<{
   gridOptions: any;
   platformNumber: any;
   formSelect: any;
+  companySelect: any;
 }>();
 
 const emit = defineEmits([ 'refresh' ]);
@@ -36,6 +37,7 @@ interface RuleForm {
   line: string[];
   ipaddress: string;
   company: string;
+  belongsCompany: string;
   companyEnglishName: string;
   address: string; // 新增
   juridicalPerson: string; // 新增
@@ -64,6 +66,7 @@ const ruleForm = reactive<RuleForm>({
   line: [],
   ipaddress: '',
   company: '',
+  belongsCompany: '',
   companyEnglishName: '',
   address: '',
   juridicalPerson: '',
@@ -110,6 +113,7 @@ const rules = reactive<FormRules<RuleForm>>({
   company: [
     { message: 'Please input activity form', trigger: 'blur' }
   ],
+  belongsCompany: [ { required: true, message: 'Please Select belongsCompany', trigger: 'change' } ],
   companyEnglishName: [
     { message: 'Please input Company English Name', trigger: 'blur' }
   ],
@@ -157,6 +161,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
     if (valid) {
       await useResponse({ id: gridOptions.data[0].id, partial: 1, formData: ruleForm }, api.updateShopDetail, loading);
       isOpen.value = false;
+      ElMessage.success('编辑成功');
       emit('refresh');
     } else {
       console.log('error submit!', fields);
@@ -236,6 +241,16 @@ function replaceCol() {
       <el-form-item label="公司" prop="company">
         <el-input v-model="ruleForm.company"/>
       </el-form-item>
+      <el-form-item label="关联公司" prop="belongsCompany">
+        <el-select v-model="ruleForm.belongsCompany" placeholder="请选择所属公司">
+          <el-option
+              v-for="item in companySelect"
+              :key="item.id"
+              :label="item.company"
+              :value="item.id">
+          </el-option>
+        </el-select>
+      </el-form-item>
       <el-form-item label="公司英文名称" prop="companyEnglishName">
         <el-input v-model="ruleForm.companyEnglishName"/>
       </el-form-item>

+ 1 - 1
src/views/shop-information/components/PlatformDetail.vue

@@ -85,7 +85,7 @@ function handleNavigate(item: any) {
                 style="height: 120px; width: 120px;">
         <template #error>
           <div class="mr-3.5 flex items-center justify-center text-5xl"
-               style="height: 130px; width: 130px; background-color: #f5f5f5">
+               style="height: 100%; width: 100%; background-color: #f5f5f5">
             <el-icon>
               <icon-picture/>
             </el-icon>

+ 13 - 5
src/views/shop-information/components/ShopDetail.vue

@@ -8,7 +8,7 @@
 import { Edit, Monitor, Picture as IconPicture, RefreshLeft, Timer } from '@element-plus/icons-vue';
 import { useResponse } from '/@/utils/useResponse';
 import * as api from '/@/views/shop-information/api';
-import { computerColumns, historyColumns, shopCurrentColumns } from '/@/views/shop-information/useColumns';
+import { computerColumns, historyColumns, shopCurrentColumns, companySelect } from '/@/views/shop-information/useColumns';
 import { useTableData } from '/@/utils/useTableData';
 import { usePagination } from '/@/utils/usePagination';
 import EditDrawer from './EditDrawer.vue';
@@ -59,6 +59,11 @@ const formSelect = ref<{ country: string[], line: string[] }>({
   line: []
 });
 
+// const companySelect = ref<{ id: string, company: string }>({
+//   id: '',
+//   company: ''
+// });
+
 onBeforeMount(() => {
   fetchShopDetailOverview();
   handleTabChange(selectedTab.value);
@@ -68,6 +73,8 @@ onBeforeMount(() => {
 async function fetchSelect() {
   const res = await useResponse({}, api.getShopSelect);
   formSelect.value = res.data;
+  // const ret = await useResponse({}, api.getCompanySelect);
+  // companySelect.value = ret.data;
 }
 
 async function fetchShopDetailOverview() {
@@ -111,7 +118,7 @@ function handleRefresh() {
 
 <template>
   <div class="p-2.5">
-    <el-card v-loading="overviewLoading" body-class="flex items-center" shadow="hover" style="border: none">
+    <el-card v-loading="overviewLoading" body-class="flex items-center gap-10" shadow="hover" style="border: none">
       <div v-if="platformNumber" class="artistic-text-container mr-7 ">
         <div class="artistic-text">
           {{ platformNumber }}
@@ -120,14 +127,14 @@ function handleRefresh() {
       <el-image v-else class="mr-7 rounded-2xl" src="" style="height: 120px; width: 120px; object-fit: contain">
         <template #error>
           <div class="mr-3.5 flex items-center justify-center text-5xl"
-               style="height: 120px; width: 120px; background-color: #f5f5f5">
+               style="height: 100%; width: 100%; background-color: #f5f5f5">
             <el-icon>
               <icon-picture/>
             </el-icon>
           </div>
         </template>
       </el-image>
-      <div class="text-lg mr-10">
+      <div class="text-lg">
         <div class="font-semibold">
           平台编号:
           <span class="font-medium italic ml-1.5" style="color: #64748b"> 
@@ -218,7 +225,7 @@ function handleRefresh() {
         </template>
         <template #toolbar_tools>
           <div class="mr-2.5">
-            <el-button :icon="Edit" plain round type="warning" @click="drawerOpen" :disabled="selectedTab != '1'">编辑</el-button>
+            <el-button :icon="Edit" plain circle type="warning" @click="drawerOpen" :disabled="selectedTab != '1'"></el-button>
           </div>
         </template>
         <template #pager>
@@ -236,6 +243,7 @@ function handleRefresh() {
         v-if="isOpen"
         v-model="isOpen"
         :formSelect
+        :companySelect
         :gridOptions="gridOptions"
         :platformNumber
         @refresh="handleRefresh"

+ 4 - 2
src/views/shop-information/index.vue

@@ -26,11 +26,13 @@ async function initData() {
 </script>
 
 <template>
-  <div class="px-2.5">
+  <div class="flex flex-col px-5">
     <el-divider content-position="left">
       <div class="font-bold text-xl">店铺信息概览</div>
     </el-divider>
-    <el-card v-loading="loading" shadow="never" style="border: none; min-height: 692px;">
+
+    <!-- 添加 flex-grow 类,确保 el-card 占据剩余空间 -->
+    <el-card v-loading="loading" class="flex-grow" shadow="never">
       <InfoCard></InfoCard>
     </el-card>
   </div>

+ 23 - 2
src/views/shop-information/useColumns.tsx

@@ -1,8 +1,21 @@
 import { useCountryInfoStore } from '/@/stores/countryInfo';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '/@/views/shop-information/api';
 
 
 const countryInfoStore = useCountryInfoStore();
 
+export const companySelect: Ref<any[]> = ref([]);
+// const ret = await useResponse({}, api.getCompanySelect);
+// companySelect.value = ret.data;
+
+async function main() {
+  const result = await api.getCompanySelect();
+  companySelect.value = result.data;
+}
+
+main();
+
 export const platformColumns = [
   { type: 'seq', width: 50, align: 'center', fixed: 'left' },
   {
@@ -268,6 +281,15 @@ export const shopCurrentColumns = [
       }
     }
   },
+  {
+    field: 'belongsCompany', title: '关联公司', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        const company = companySelect.value.find(c => c.id === row.belongsCompany);
+        return <span class="font-medium">{ company ? company.company : '--' }</span>;
+      }
+    }
+  },
   {
     field: 'company', title: '公司名称', minWidth: 'auto', align: 'center',
     slots: {
@@ -355,7 +377,6 @@ export const shopCurrentColumns = [
     field: 'shopPhoneAndName', title: '主账户电话', minWidth: 'auto', align: 'center',
     slots: {
       default({ row }: any) {
-        console.log('Row data:', row); // 打印行数据,检查是否正确
         return <span class={ 'font-medium' }>{ row.shopPhoneAndName !== null ? row.shopPhoneAndName : '--' }</span>;
       }
     }
@@ -544,7 +565,7 @@ export const historyColumns: any = [
             class={ 'font-medium' }>{ row.receivablesAccountCompany ? row.receivablesAccountCompany : '--' }</span>;
       }
     }
-  },  
+  },
   {
     field: 'vatNumber', title: 'VAT税号', minWidth: 'auto', align: 'center',
     slots: {

+ 5 - 1
src/views/system/menu/components/MenuTreeCom/index.vue

@@ -31,7 +31,11 @@
 			<template #default="{ node, data }">
 				<element-tree-line :node="node" :showLabelLine="false" :indent="32">
 					<span v-if="data.status" class="text-center font-black font-normal">
-						<SvgIcon :name="node.data.icon" color="var(--el-color-primary)" />
+						<SvgIcon :name="node.data.icon" color="var(--el-color-primary)" :style="[
+                  node.data.icon.startsWith('el') ? {
+                    'top': '2px',
+                  } : {}
+                ]" />
 						&nbsp;{{ node.label }}
 					</span>
 					<span v-else class="text-center font-black text-red-700 font-normal"> <SvgIcon :name="node.data.icon" />&nbsp;{{ node.label }} </span>

+ 13 - 13
src/views/test/index.vue

@@ -1,20 +1,20 @@
 <script setup>
 
-import PropsTest from './propsTest.vue';
-import EditDrawer from '/@/views/shop-information/components/EditDrawer.vue';
+// import PropsTest from './propsTest.vue';
+// import EditDrawer from '/@/views/shop-information/components/EditDrawer.vue';
 
-const text = ref('1');
-const isOpen = ref(false); // 定义 isOpen
-
-function handleOpen() {
-  isOpen.value = true; // 点击按钮后打开 dialog
-}
+// const text = ref('1');
+// const isOpen = ref(false); // 定义 isOpen
+//
+// function handleOpen() {
+//   isOpen.value = true; // 点击按钮后打开 dialog
+// }
 </script>
 
 <template>
-  <el-button @click="handleOpen">确定</el-button>
-
-  <!-- 传递 isOpen 给子组件 -->
-  <!--<PropsTest :text="text" v-model="isOpen" />-->
-  <el-tag type="warning"></el-tag>
+  <!--<el-button @click="handleOpen">确定</el-button>-->
+  
+  <!--&lt;!&ndash; 传递 isOpen 给子组件 &ndash;&gt;-->
+  <!--&lt;!&ndash;<PropsTest :text="text" v-model="isOpen" />&ndash;&gt;-->
+  <!--<el-tag type="warning"></el-tag>-->
 </template>

+ 7 - 7
vite.config.ts

@@ -27,12 +27,6 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
       vue(),
       vueJsx(),
       // vueSetupExtend()
-      compression({
-        algorithm: 'gzip', // 使用 gzip 压缩
-        ext: '.gz', // 输出的文件扩展名
-        threshold: 10240, // 只有大小大于该值的资源会被压缩(默认 10KB)
-        deleteOriginFile: false, // 是否删除原始未压缩的文件
-      }),
       AutoImport({
         imports: [
           'vue',
@@ -51,6 +45,12 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
       Components({
         // resolvers: [ElementPlusResolver()],
       }),
+      compression({
+        algorithm: 'gzip', // 使用 gzip 压缩
+        ext: '.gz', // 输出的文件扩展名
+        threshold: 10240, // 只有大小大于该值的资源会被压缩(默认 10KB)
+        deleteOriginFile: false, // 是否删除原始未压缩的文件
+      }),
       lazyImport({
         resolvers: [
           VxeResolver({
@@ -95,7 +95,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
             vue: [ 'vue', 'vue-router', 'pinia' ],
             echarts: [ 'echarts' ],
             elementPlus: ['element-plus'],
-          },
+          }
           // manualChunks(id) {
           //   if (id.includes('node_modules')) {
           //     return 'vendor';