瀏覽代碼

fix:开始第一次提交

贾宇博 2 周之前
父節點
當前提交
6634f6b345

+ 3 - 0
.env.development

@@ -6,3 +6,6 @@ VITE_APP_ENV = 'development'
 
 # 若依管理系统/开发环境
 VITE_APP_BASE_API = '/dev-api'
+
+#地图key
+VITE_TX_MAP_KEY = KFEBZ-P2GKZ-A5PX4-7Q6Y7-KXOBF-XCB4C

File diff suppressed because it is too large
+ 3 - 1
README.md


+ 1 - 0
package.json

@@ -31,6 +31,7 @@
     "nprogress": "0.2.0",
     "pinia": "2.1.7",
     "splitpanes": "3.1.5",
+    "tlbs-map-vue": "^1.3.1",
     "vue": "3.4.31",
     "vue-cropper": "1.1.1",
     "vue-router": "4.4.0",

+ 19 - 0
pnpm-lock.yaml

@@ -53,6 +53,9 @@ importers:
       splitpanes:
         specifier: 3.1.5
         version: 3.1.5
+      tlbs-map-vue:
+        specifier: ^1.3.1
+        version: 1.3.1(vue@3.4.31)
       vue:
         specifier: 3.4.31
         version: 3.4.31
@@ -1881,6 +1884,16 @@ packages:
   tiny-emitter@2.1.0:
     resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
 
+  tlbs-map-vue@1.3.1:
+    resolution: {integrity: sha512-oh239gdtis/b6VInlmFuxP3fnwssEG4Hy5sTzLyiJ4bTeqR8pB7MPzl81AqaL+fC4W7aFsSkofbBGmgh4rD/4w==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      '@vue/composition-api': ^1.4.9
+      vue: ^2.6.0 || >=3.0.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
   to-object-path@0.3.0:
     resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==}
     engines: {node: '>=0.10.0'}
@@ -4045,6 +4058,12 @@ snapshots:
 
   tiny-emitter@2.1.0: {}
 
+  tlbs-map-vue@1.3.1(vue@3.4.31):
+    dependencies:
+      fs-extra: 10.1.0
+      vue: 3.4.31
+      vue-demi: 0.14.10(vue@3.4.31)
+
   to-object-path@0.3.0:
     dependencies:
       kind-of: 3.2.2

+ 30 - 0
src/api/index.js

@@ -0,0 +1,30 @@
+import request from '@/utils/request'
+
+// 订单总览
+export const orderSummary = (data) => {
+  return request({
+    url: `/core/org/stat/orderSummary`,
+    method: 'post',
+    data
+  })
+}
+// 人员总览
+export const userSummary = (data) => {
+    return request({
+      url: `/core/org/stat/userSummary`,
+      method: 'post',
+      data
+    })
+  }
+
+//机构排名
+export const orgRank = (data) => {
+    return request({
+      url: `/core/org/stat/orgRank`,
+      method: 'post',
+      data
+    })
+  }
+
+
+  

+ 9 - 0
src/api/map.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+// 获取路由
+export const getMapCoder = (address) => {
+  return request({
+    url: `/ws/geocoder/v1/?address=${address}&key=${import.meta.env.VITE_TX_MAP_KEY}`,
+    method: 'get'
+  })
+}

+ 8 - 0
src/api/order/manage.js

@@ -6,4 +6,12 @@ export function list(query) {
     method: 'get',
     params: query
   })
+}
+
+
+export function orderInfo(mainOrderId) {
+  return request({
+    url: `/core/users/orders/web/orderInfo/${mainOrderId}`,
+    method: 'get',
+  })
 }

+ 23 - 0
src/api/system/banner.js

@@ -0,0 +1,23 @@
+import request from '@/utils/request'
+export function add(data) {
+  return request({
+    url: '/web/core/slideshow',
+    method: 'post',
+    data: data
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/web/core/slideshow/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delbanner(slideshowIds) {
+  return request({
+    url: '/web/core/slideshow/' + slideshowIds,
+    method: 'delete'
+  })
+}

+ 17 - 0
src/api/system/classification.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+// 查询部门列表
+export function listClass(query) {
+  return request({
+    url: '/core/business/management/getTreeList?parentId=0',
+    method: 'get',
+    params: query
+  })
+}
+export function updateClass(data) {
+  return request({
+    url: '/core/business/management/web/updateTree',
+    method: 'put',
+    data: data
+  })
+}

+ 58 - 20
src/components/FileUpload/index.vue

@@ -1,25 +1,18 @@
 <template>
   <div class="upload-file">
-    <el-upload
-      multiple
-      :action="uploadFileUrl"
-      :before-upload="handleBeforeUpload"
-      :file-list="fileList"
-      :limit="limit"
-      :on-error="handleUploadError"
-      :on-exceed="handleExceed"
-      :on-success="handleUploadSuccess"
-      :show-file-list="false"
-      :headers="headers"
-      class="upload-file-uploader"
-      ref="fileUpload"
-      v-if="!disabled"
-    >
+    <el-upload multiple :action="uploadFileUrl" :before-upload="handleBeforeUpload" :file-list="fileList" :limit="limit"
+      :on-error="handleUploadError" :on-exceed="handleExceed" :on-success="handleUploadSuccess" :show-file-list="false"
+      :headers="headers" class="upload-file-uploader" ref="fileUpload" v-if="!disabled" :class="model === 'avatarCard'&&'avatar-uploader'">
+      <div v-if="model === 'avatarCard'">
+        <img v-if="avatarFile" :src="avatarFile.url" class="avatar" />
+         <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
+      </div>
       <!-- 上传按钮 -->
-      <el-button type="primary">选取文件</el-button>
+      <el-button type="primary" v-else>选取文件</el-button>
     </el-upload>
-    <!-- 上传提示 -->
-    <div class="el-upload__tip" v-if="showTip && !disabled">
+   <view v-if="model === 'btn'">
+     <!-- 上传提示 -->
+     <div class="el-upload__tip" v-if="showTip && !disabled">
       请上传
       <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
       <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
@@ -36,11 +29,13 @@
         </div>
       </li>
     </transition-group>
+   </view>
   </div>
 </template>
 
 <script setup>
 import { getToken } from "@/utils/auth";
+import { computed } from "vue";
 
 const props = defineProps({
   modelValue: [String, Object, Array],
@@ -68,6 +63,10 @@ const props = defineProps({
   disabled: {
     type: Boolean,
     default: false
+  },
+  model: {
+    type: String,
+    default: 'btn'
   }
 });
 
@@ -83,6 +82,10 @@ const showTip = computed(
   () => props.isShowTip && (props.fileType || props.fileSize)
 );
 
+const avatarFile = computed(()=>{
+  return fileList.value[fileList.value.length-1]
+})
+
 watch(() => props.modelValue, val => {
   if (val) {
     let temp = 1;
@@ -100,7 +103,7 @@ watch(() => props.modelValue, val => {
     fileList.value = [];
     return [];
   }
-},{ deep: true, immediate: true });
+}, { deep: true, immediate: true });
 
 // 上传前校检格式和大小
 function handleBeforeUpload(file) {
@@ -145,7 +148,7 @@ function handleUploadError(err) {
 // 上传成功回调
 function handleUploadSuccess(res, file) {
   if (res.code === 200) {
-    uploadList.value.push({ name: res.fileName, url: res.fileName });
+    uploadList.value.push({ name: res.fileName, url: res.url });
     uploadedSuccessfully();
   } else {
     number.value--;
@@ -200,19 +203,54 @@ function listToString(list, separator) {
 .upload-file-uploader {
   margin-bottom: 5px;
 }
+
 .upload-file-list .el-upload-list__item {
   border: 1px solid #e4e7ed;
   line-height: 2;
   margin-bottom: 10px;
   position: relative;
 }
+
 .upload-file-list .ele-upload-list__item-content {
   display: flex;
   justify-content: space-between;
   align-items: center;
   color: inherit;
 }
+
 .ele-upload-list__item-content-action .el-link {
   margin-right: 10px;
 }
 </style>
+
+
+<style scoped>
+.avatar-uploader .avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+</style>
+
+<style>
+.avatar-uploader .el-upload {
+  border: 1px dashed var(--el-border-color);
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  transition: var(--el-transition-duration-fast);
+}
+
+.avatar-uploader .el-upload:hover {
+  border-color: var(--el-color-primary);
+}
+
+.el-icon.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  text-align: center;
+}
+</style>

+ 3 - 0
src/main.js

@@ -43,6 +43,8 @@ import ImagePreview from "@/components/ImagePreview"
 // 字典标签组件
 import DictTag from '@/components/DictTag'
 
+import TlbsMap from 'tlbs-map-vue';
+
 const app = createApp(App)
 
 // 全局方法挂载
@@ -68,6 +70,7 @@ app.use(router)
 app.use(store)
 app.use(plugins)
 app.use(elementIcons)
+app.use(TlbsMap)
 app.component('svg-icon', SvgIcon)
 
 directive(app)

+ 14 - 0
src/router/index.js

@@ -157,6 +157,20 @@ export const dynamicRoutes = [
         meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
       }
     ]
+  },
+  {
+    path: '/order',
+    component: Layout,
+    hidden: true,
+    permissions: [],
+    children: [
+      {
+        path: 'order-details/:orderId(\\d+)',
+        component: () => import('@/views/order/details/index'),
+        name: 'Data',
+        meta: { title: '订单详情', activeMenu: '/order/order-manage' }
+      }
+    ]
   }
 ]
 

+ 3 - 1
src/views/components/DialogForm/index.vue

@@ -4,7 +4,9 @@
             <template v-for="item in column" :key="item.prop">
                 <!-- 输入框 -->
                 <el-form-item :label="item.label" v-if="item.type === 'input'" :prop="item.prop" :rules="item.rules">
-                    <el-input v-model="form[item.prop]" :placeholder="'请输入' + item.label" clearable  :disabled="disables[item.prop]"/>
+                    <el-input v-model="form[item.prop]" :placeholder="'请输入' + item.label" clearable  :disabled="disables[item.prop]">
+                        <template v-if="item.dese" #append>{{ item.dese }}</template>
+                    </el-input>
                 </el-form-item>
                 <!-- 文本域 -->
                 <el-form-item :label="item.label" v-if="item.type === 'textarea'" :prop="item.prop" :rules="item.rules">

+ 122 - 0
src/views/components/Map/index.vue

@@ -0,0 +1,122 @@
+<template>
+    <div class="map-container">
+        <tlbs-map ref="mapRef" api-key="KFEBZ-P2GKZ-A5PX4-7Q6Y7-KXOBF-XCB4C" :center="center" :zoom="zoom"
+            :control="control" @click="onClick" @map_inited="onMapInited">
+            <tlbs-multi-marker ref="markerRef" :geometries="geometries" :styles="styles" :options="options" />
+        </tlbs-map>
+    </div>
+</template>
+<script setup>
+import { ref, watch } from 'vue';
+import { onMounted } from 'vue';
+import { getMapCoder } from '@/api/map';
+
+
+const props = defineProps({
+    address:{
+        type: String,
+        default: ''
+    },
+    latAndLng:{
+        type: Object,
+        default: {
+            latitude: '',
+            longitude: ''
+        }
+    }
+})
+
+
+const mapRef = ref(null);
+const markerRef = ref(null);
+const center = ref({ lat: 39.91799, lng: 116.397027 });
+const zoom = ref(15);
+const control = {
+    scale: {},
+    zoom: {
+        position: 'topRight',
+    },
+};
+const geometries = ref([
+    { styleId: 'marker', position: { lat: 39.91799, lng: 116.397027 } },
+])
+const styles = {
+    marker: {
+        // width: 20,
+        // height: 30,
+        // anchor: { x: 10, y: 30 },
+    },
+};
+const options = {
+    minZoom: 3,
+    maxZoom: 15,
+}
+const onClick = (e) => {
+    console.log(e);
+};
+
+const onMapInited = () => {
+    // 地图加载完成后,可以获取地图实例、点标记实例,调用地图实例、点标记实例方法
+    console.log(mapRef.value.map);
+    console.log(markerRef.value.marker);
+};
+
+const getLayerInstance = () => {
+    // 可以获取点标记实例,调用点标记实例方法
+    console.log(markerRef.value.marker);
+};
+// https://apis.map.qq.com/ws/geocoder/v1/?address=
+const adressToCode = async() => {
+    try {
+        const res= await getMapCoder(props.address);
+        if(res.status ===0){
+            const location = res.result.location;
+            center.value = location
+            geometries.value = [ { styleId: 'marker', position: location }]
+        }
+        console.log(12,res);
+
+    } catch (error) {
+        console.log('error',error);
+        
+    }
+}
+
+watch(()=> props.address, () => {
+    console.log('props.address',props.address);
+    props.address && adressToCode();
+})
+
+onMounted(() => {
+    const {latitude,longitude} =props.latAndLng;
+    if(latitude&&longitude){
+
+        center.value = { lat: Number(latitude), lng: Number(longitude) }
+        geometries.value = [ { styleId: 'marker', position: { lat: Number(latitude), lng: Number(longitude) } }]
+    }
+})
+
+</script>
+
+<style>
+.map-container {
+  width: 100%;
+  height: 100%;
+}
+</style>
+<style scoped>
+.control-container {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1001;
+  display: flex;
+  align-items: center;
+}
+button {
+  padding: 4px;
+  background-color: #fff;
+  margin-right: 5px;
+  border: 1px solid #ddd;
+}
+</style>

File diff suppressed because it is too large
+ 265 - 1061
src/views/index.vue


+ 39 - 19
src/views/order/details/index.vue

@@ -1,16 +1,19 @@
 <template>
     <div class="details-main">
         <div class="details-main-box">
-            <div class="details-dr">1</div>
+            <div class="details-dr">
+                <Map :address="detailsData.address" :latAndLng="{latitude:detailsData.latitude,longitude:detailsData.longitude}"/>
+            </div>
             <div class="details-list">
-                <div class="details-list-item">
+                <div class="details-list-item" v-for="item in detailsData.secondOrderList" :key="item.secondOrderId">
                     <el-image style="width: 100px; height: 100px"
-                        src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
+                        :src="item.volunteerPicture" />
                     <div>
-                        <div>姓名:xxx</div>
-                        <div>评分:xxx</div>
-                        <div> 类别:xxx</div>
-                        <div> 技能简介:xxx</div>
+                        <div>姓名:{{ detailsData.clientName }}</div>
+                        <div>评分:{{ item.userReview }}</div>
+                        <div>类别:{{ detailsData.businessTierName }}</div>
+                        <div style="display: flex;">订单状态:<dict-tag :options="lrr_service_status" :value="item.orderStatus" /></div>
+
                     </div>
 
                 </div>
@@ -19,17 +22,14 @@
         </div>
         <div class="details-main-footer">
             <el-descriptions title="订单信息" border>
-                <el-descriptions-item label="姓名">kooriookami</el-descriptions-item>
-                <el-descriptions-item label="项目类别">18100000000</el-descriptions-item>
-                <el-descriptions-item label="被服务人员">18100000000</el-descriptions-item>
-                <!-- <el-descriptions-item label="是否有疾病">18100000000</el-descriptions-item> -->
-                <el-descriptions-item label="手机号">18100000000</el-descriptions-item>
-                <el-descriptions-item label="科目">18100000000</el-descriptions-item>
-                <el-descriptions-item label="性别">18100000000</el-descriptions-item>
-                <el-descriptions-item label="服务项目">18100000000</el-descriptions-item>
-                <el-descriptions-item label="订单状态">18100000000</el-descriptions-item>
-                <el-descriptions-item label="电话">18100000000</el-descriptions-item>
-                <el-descriptions-item label="住址">18100000000</el-descriptions-item>
+                <el-descriptions-item label="姓名">{{ detailsData.volunteerName  }}</el-descriptions-item>
+                <el-descriptions-item label="项目类别">{{ detailsData.businessTierName }}</el-descriptions-item>
+                <el-descriptions-item label="被服务人员">{{ detailsData.clientName }}</el-descriptions-item>
+                <el-descriptions-item label="手机号">{{ detailsData.clientTelephone }}</el-descriptions-item>
+                <el-descriptions-item label="服务项目">{{ detailsData.volunteerName }}</el-descriptions-item>
+                <el-descriptions-item label="订单状态"><dict-tag :options="lrr_service_status" :value="detailsData.orderStatus" /> </el-descriptions-item>
+                <el-descriptions-item label="电话">{{ detailsData.clientTelephone }}</el-descriptions-item>
+                <el-descriptions-item label="住址">{{ detailsData.address }}</el-descriptions-item>
             </el-descriptions>
         </div>
 
@@ -37,12 +37,29 @@
 </template>
 
 <script setup>
-
+import Map from '@/views/components/Map/index.vue'
+import { orderInfo } from "@/api/order/manage.js";
+import { onMounted } from 'vue';
+const { proxy } = getCurrentInstance();
+const { lrr_service_status } = proxy.useDict("lrr_service_status");
 const list = [
     {
         lable: ''
     }
 ]
+const route = useRoute()
+const detailsData = ref({})
+const getDetails = async()=> {
+    try {
+        
+        const res = await orderInfo(route.params.orderId);
+        console.log('res',res);
+        detailsData.value = res.data;
+    } catch (error) {
+        
+    }
+}
+onMounted(getDetails)
 </script>
 
 <style lang="scss" scoped>
@@ -51,6 +68,7 @@ const list = [
     display: flex;
     flex-direction: column;
     padding: 24px;
+    overflow-y: auto;
 
     .details-main-box {
         display: flex;
@@ -58,12 +76,14 @@ const list = [
 
         .details-dr {
             flex: 1;
+  
         }
 
         .details-list {
             display: flex;
             flex-direction: column;
             overflow-y: auto;
+            width: 300px;
 
             .details-list-item {
                 display: flex;

+ 4 - 3
src/views/order/manage/index.vue

@@ -12,11 +12,11 @@ import { ref } from 'vue';
 import ListPage from '@/views/components/ListPage/index.vue';
 import { list } from "@/api/order/manage.js";
 import { getTreeList } from "@/api/staff/price.js";
-
 import DialogForm from '@/views/components/DialogForm/index.vue';
+
 const { proxy } = getCurrentInstance();
 const { lrr_service_status } = proxy.useDict("lrr_service_status");
-
+const router = useRouter();
 const options = ref([]);
 const userTableRef = ref();
 const dialogFormRef = ref(null);
@@ -116,7 +116,8 @@ const listPageData = reactive({
             key: 'details',
             func: (row) => {
                 console.log(row)
-                openDialog(row)
+                router.push("/order/order-details/" + row.mainOrderId);
+                // openDialog(row)
             }
         },
         // {

+ 5 - 9
src/views/staff/user/index.vue

@@ -24,7 +24,7 @@ const listPageData = reactive({
     tableColumn: [
         {
             label: '用户ID',
-            prop: 'roleId',
+            prop: 'userId',
              width: '180px'
         },
         {
@@ -58,14 +58,10 @@ const listPageData = reactive({
             type: 'input',
             isSearch: true
         },
-        // {
-        //     label: '是否有疾病',
-        //     prop: 'isContagion',
-        // },
-        // {
-        //     label: '住址',
-        //     prop: 'adress',
-        // },
+        {
+            label: '住址',
+            prop: 'adress',
+        },
     ],
     searchBtns: [],
     tableApi: listUser,//接口地址

+ 15 - 11
src/views/staff/volunteer/manage/index.vue

@@ -164,28 +164,30 @@ const dialogData = reactive({
             label: '家庭地址',
             prop: 'address',
             type: 'input',
-
         },
         {
             label: '服务项目/类别',
-            prop: 'businessManagementId',
-            type: 'cascader',
-            isSearch: true,
+            prop: 'businessTierName',
+            type: 'input',
             options: options,
             props: { label: 'businessName', value: 'id', checkStrictly:true },
-
         },
         {
-            label: '身份证件',
-            prop: 'idCardPicture',
-            type: 'img',
-
+            label: '服务时长',
+            prop: 'businessDuration',
+            type: 'input',
+            dese:'分钟'
+        },
+        {
+            label: '服务价格',
+            prop: 'businessPrice',
+            type: 'input',
+            dese:'元'
         },
         {
             label: '职业证书',
             prop: 'certificationPicture',
             type: 'img',
-
         },
         {
             label: '技能简介',
@@ -233,7 +235,9 @@ const openDialog = (row, type) => {
             phonenumber: true,
             address: true,
             skillDescribe: true,
-            businessManagementId: true,
+            businessTierName: true,
+            businessDuration:true,
+            businessPrice:true
         }
 
         if (type) {

+ 141 - 0
src/views/system/banner/index.vue

@@ -0,0 +1,141 @@
+<template>
+    <div>
+        <ListPage tableKey="businessbannerId" :column="listPageData.tableColumn" :tableApi="listPageData.tableApi"
+            :isSelect="listPageData.isSelect" :scopeBtns="listPageData.scopeBtns" :searchBtns="listPageData.searchBtns"
+            ref="userTableRef" />
+
+        <el-dialog :title="title" v-model="open" width="500" :before-close="cancel">
+            <div style="padding: 20px;">
+                <FileUpload :fileType="['png', 'jpg', 'jpeg']" @update:modelValue="fileUpdata" :modelValue="files"/>
+            </div>
+            <template #footer>
+                <div class="dialog-footer">
+                    <el-button type="primary" @click="submitForm">确 定</el-button>
+                    <el-button @click="cancel">取 消</el-button>
+                </div>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref, render } from 'vue';
+import ListPage from '@/views/components/ListPage/index.vue';
+import { add, list,delbanner } from "@/api/system/banner";
+
+const { proxy } = getCurrentInstance();
+
+const userTableRef = ref();
+const uploadRef = ref([]);
+const listPageData = reactive({
+    tableColumn: [
+         {
+            label: '创建人',
+            prop: 'createBy',
+            type:'input',
+            isSearch: true,
+        },
+        {
+            label: '创建时间',
+            prop: 'createTime',
+
+        },
+        {
+            label: '图片',
+            prop: 'picture',
+            type: 'img',
+        },
+
+    ],
+    searchBtns: [
+        {
+            label: '新增',
+            func: () => {
+                console.log('新增')
+                openDialog();
+            },
+            key: 'add',
+            hasPermi: ['banner:add'],
+            icon: 'Plus',
+            type: 'primary'
+        }
+    ],
+    tableApi: list,//接口地址
+    isSelect: false,//是否勾选
+    scopeBtns: [
+        {
+            label: '编辑',
+            type: 'primary',
+            hasPermi: ['banner:edit'],
+            key: 'edit',
+            func: (row) => {
+                console.log(row)
+                openDialog(row);
+
+            }
+        },
+        {
+            label: '删除',
+            type: 'danger',
+            hasPermi: ['banner:delete'],
+            key: 'edit',
+            func: (row) => {
+                console.log(row)
+                handleDelete([row.slideshowId])
+            }
+        }
+    ]
+})
+
+const open = ref(false);
+const files = ref('');
+const fileUpdata = (file) => {
+    console.log(file);
+    files.value = file;
+}
+
+const openDialog = async (row) => {
+
+    try {
+        if(row){
+            files.value = row.picture;
+        }
+
+    } catch (error) {
+
+
+
+    } finally {
+        open.value = true;
+    }
+}
+
+const handleDelete = (ids) => {
+    console.log('ids', ids);
+
+    proxy.$modal.confirm('是否确认删除编号为"' + ids + '"的数据项?').then(function () {
+        return delbanner(ids)
+    }).then(() => {
+        userTableRef.value.resetForm();
+    }).catch(() => { });
+}
+const submitForm = () => {
+    console.log('submit', files.value);
+    const parmas = JSON.parse(JSON.stringify(files.value))
+    add({picture: parmas }).then(res => {
+        if (res.code === 200) {
+            proxy.$modal.msgSuccess("新增成功");
+            cancel()
+            return;
+        }
+        proxy.$modal.msgSuccess(res.msg);
+    })
+}
+const cancel = () => {
+    userTableRef.value.resetForm();
+    files.value ='';
+    open.value = false;
+}
+</script>
+
+<style lang='scss' scoped></style>

+ 231 - 0
src/views/system/classification/index.vue

@@ -0,0 +1,231 @@
+<template>
+   <div class="app-container">
+      <!-- <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
+         <el-form-item label="类型名称" prop="deptName">
+            <el-input
+               v-model="queryParams.deptName"
+               placeholder="请输入类型名称"
+               clearable
+               style="width: 200px"
+               @keyup.enter="handleQuery"
+            />
+         </el-form-item>
+         <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+            <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+         </el-form-item>
+      </el-form> -->
+
+      <!-- <el-row :gutter="10" class="mb8">
+         <el-col :span="1.5">
+            <el-button
+               type="primary"
+               plain
+               icon="Plus"
+               @click="handleAdd"
+            >新增</el-button>
+         </el-col>      
+      </el-row> -->
+
+      <el-table
+         v-if="refreshTable"
+         v-loading="loading"
+         :data="deptList"
+         row-key="id"
+         :default-expand-all="isExpandAll"
+         :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+      >
+         <el-table-column prop="businessName" label="分类名称" width="260"></el-table-column>
+         <el-table-column prop="businessName" label="分类图片" width="260">
+            <template #default="scope">
+               <el-image style="width: 100px; height: 100px" :src="scope.row.businessIcon" fit="fill" />
+            </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="handleUpdate(scope.row)" >修改</el-button>
+               <!-- <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" >新增</el-button> -->
+            </template>
+         </el-table-column>
+      </el-table>
+
+      <!-- 添加或修改分类对话框 -->
+      <el-dialog :title="title" v-model="open" width="600px" append-to-body>
+         <el-form ref="deptRef" :model="form" :rules="rules" label-width="80px">
+            <el-row>
+               <!-- <el-col :span="24" v-if="form.parentId !== '0'">
+                  <el-form-item label="上级分类" prop="parentId">
+                     <el-tree-select
+                        v-model="form.parentId"
+                        :data="deptOptions"
+                        :props="{ value: 'id', label: 'businessName', children: 'children' }"
+                        value-key="parentId"
+                        placeholder="选择上级分类"
+                        check-strictly
+                     />
+                  </el-form-item>
+               </el-col> -->
+               <el-col :span="24">
+                  <el-form-item label="分类名称" prop="businessName">
+                     <el-input v-model="form.businessName" placeholder="请输入类型名称" disabled="true" />
+                  </el-form-item>
+               </el-col>
+               <el-col :span="24">
+                  <el-form-item label="分类图片" prop="  ">
+                     <FileUpload :fileType="['png', 'jpg', 'jpeg']"  model="avatarCard" @update:modelValue="fileUpdata" :modelValue="files"/>
+                  </el-form-item>
+               </el-col>
+            </el-row>
+         </el-form>
+         <template #footer>
+            <div class="dialog-footer">
+               <el-button type="primary" @click="submitForm">确 定</el-button>
+               <el-button @click="cancel">取 消</el-button>
+            </div>
+         </template>
+      </el-dialog>
+   </div>
+</template>
+
+<script setup name="Dept">
+import { listClass, updateClass } from "@/api/system/classification";
+
+const { proxy } = getCurrentInstance();
+const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
+
+const deptList = ref([]);
+const open = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const title = ref("");
+const deptOptions = ref([]);
+const isExpandAll = ref(true);
+const refreshTable = ref(true);
+const files = ref('');
+
+const data = reactive({
+  form: {},
+  queryParams: {
+    deptName: undefined,
+    status: undefined
+  },
+  rules: {
+    parentId: [{ required: true, message: "上级分类不能为空", trigger: "blur" }],
+    businessName: [{ required: true, message: "类型名称不能为空", trigger: "blur" }],
+  },
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询分类列表 */
+function getList() {
+  loading.value = true;
+  listClass(queryParams.value).then(response => {
+    deptList.value = proxy.handleTree(response.data, "id");
+    loading.value = false;
+  });
+}
+
+/** 取消按钮 */
+function cancel() {
+  open.value = false;
+  reset();
+}
+
+/** 表单重置 */
+function reset() {
+  form.value = {
+    deptId: undefined,
+    parentId: undefined,
+    deptName: undefined,
+    orderNum: 0,
+    leader: undefined,
+    phone: undefined,
+    email: undefined,
+    status: "0"
+  };
+  proxy.resetForm("deptRef");
+}
+
+/** 搜索按钮操作 */
+function handleQuery() {
+  getList();
+}
+
+/** 重置按钮操作 */
+function resetQuery() {
+  proxy.resetForm("queryRef");
+  handleQuery();
+}
+
+/** 新增按钮操作 */
+function handleAdd(row) {
+   reset();
+   deptOptions.value = deptList.value;
+  if (row != undefined) {
+    form.value.parentId = row.deptId;
+  }
+  open.value = true;
+  title.value = "添加分类";
+}
+
+
+/** 修改按钮操作 */
+function handleUpdate(row) {
+   console.log('修改按钮操作',row);
+  reset();
+   deptOptions.value = deptList.value;
+   form.value = row;
+   open.value = true;
+   title.value = "修改分类";
+   files.value = row.businessIcon;
+}
+
+/** 提交按钮 */
+function submitForm() {
+  proxy.$refs["deptRef"].validate(valid => {
+    if (valid) {
+      console.log('form.value',form.value,files.value);
+      const parmas = {
+         businessManagementId: form.value.id,
+         businessIcon:files.value
+      }
+      console.log('parmas',parmas);
+      
+      if (parmas.businessManagementId) {
+        updateClass(parmas).then(response => {
+          proxy.$modal.msgSuccess("修改成功");
+          open.value = false;
+          getList();
+        });
+      } else {
+       
+      }
+    }
+  });
+}
+
+/** 删除按钮操作 */
+function handleDelete(row) {
+  proxy.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?').then(function() {
+   //  return delDept(row.deptId);
+  }).then(() => {
+    getList();
+    proxy.$modal.msgSuccess("删除成功");
+  }).catch(() => {});
+}
+
+
+
+const fileUpdata = (file) => {
+   const f = file.split(',')
+    console.log(file);
+    files.value = f[f.length-1];
+    console.log(' files.value', files.value);
+
+}
+
+getList();
+</script>
+
+

+ 9 - 9
src/views/system/user/index.vue

@@ -2,11 +2,11 @@
   <div class="app-container">
     <el-row :gutter="20">
       <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme">
-        <!--部门数据-->
+        <!--区域数据-->
         <pane size="16">
           <el-col>
             <div class="head-container">
-              <el-input v-model="deptName" placeholder="请输入部门名称" clearable prefix-icon="Search" style="margin-bottom: 20px" />
+              <el-input v-model="deptName" placeholder="请输入区域名称" clearable prefix-icon="Search" style="margin-bottom: 20px" />
             </div>
             <div class="head-container">
               <el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" />
@@ -61,7 +61,7 @@
               <el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
               <el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
               <el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
-              <el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
+              <el-table-column label="区域" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
               <el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
               <el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
                 <template #default="scope">
@@ -111,8 +111,8 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="归属部门" prop="deptId">
-              <el-tree-select v-model="form.deptId" :data="enabledDeptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属部门" check-strictly />
+            <el-form-item label="归属区域" prop="deptId">
+              <el-tree-select v-model="form.deptId" :data="enabledDeptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属区域" check-strictly />
             </el-form-item>
           </el-col>
         </el-row>
@@ -261,7 +261,7 @@ const columns = ref([
   { key: 0, label: `用户编号`, visible: true },
   { key: 1, label: `用户名称`, visible: true },
   { key: 2, label: `用户昵称`, visible: true },
-  { key: 3, label: `部门`, visible: true },
+  { key: 3, label: `区域`, visible: true },
   { key: 4, label: `手机号码`, visible: true },
   { key: 5, label: `状态`, visible: true },
   { key: 6, label: `创建时间`, visible: true }
@@ -294,7 +294,7 @@ const filterNode = (value, data) => {
   return data.label.indexOf(value) !== -1;
 };
 
-/** 根据名称筛选部门树 */
+/** 根据名称筛选区域树 */
 watch(deptName, val => {
   proxy.$refs["deptTreeRef"].filter(val);
 });
@@ -309,7 +309,7 @@ function getList() {
   });
 };
 
-/** 查询部门下拉树结构 */
+/** 查询区域下拉树结构 */
 function getDeptTree() {
   deptTreeSelect().then(response => {
     deptOptions.value = response.data;
@@ -317,7 +317,7 @@ function getDeptTree() {
   });
 };
 
-/** 过滤禁用的部门 */
+/** 过滤禁用的区域 */
 function filterDisabledDept(deptList) {
   return deptList.filter(dept => {
     if (dept.disabled) {

+ 9 - 2
vite.config.js

@@ -29,13 +29,20 @@ export default defineConfig(({ mode, command }) => {
       host: true,
       open: true,
       proxy: {
+        '/dev-api/ws': {
+          // target: 'http://localhost:9527',
+          target: 'https://apis.map.qq.com',
+          changeOrigin: true,
+          rewrite: (p) => p.replace('/dev-api/ws', '/ws')
+        },
         // https://cn.vitejs.dev/config/#server-proxy
         '/dev-api': {
           // target: 'http://localhost:9527',
-        target: 'http://192.168.100.133:9527',
+          target: 'http://192.168.100.121:9527',
           changeOrigin: true,
           rewrite: (p) => p.replace(/^\/dev-api/, '')
-        }
+        },
+        
       }
     },
     //fix:error:stdin>:7356:1: warning: "@charset" must be the first rule in the file