Browse Source

fix 骑手添加附件 弹窗2次
fix 擅长类型下拉

tea 3 months ago
parent
commit
9a3950cd74

+ 159 - 46
kxmall-admin-ui/src/views/product/storeProduct/index.vue

@@ -29,14 +29,16 @@
           icon="el-icon-plus"
           size="mini"
           @click="handleAdd"
-        >新增</el-button>
+        >新增
+        </el-button>
         <el-button
           v-hasPermi="['product:storeProductRule:edit']"
           size="mini"
           type="primary"
           :disabled="multiple"
           @click="onAuthorize"
-        >授权商品到仓库</el-button>
+        >授权商品到仓库
+        </el-button>
         <!-- 新增导入按钮 -->
         <el-button
           type="info"
@@ -44,7 +46,8 @@
           icon="el-icon-upload2"
           size="mini"
           @click="handleImport"
-        >导入</el-button>
+        >导入
+        </el-button>
       </el-form-item>
     </el-form>
 
@@ -89,14 +92,16 @@
             type="text"
             icon="el-icon-edit"
             @click="handleUpdate(row)"
-          >修改</el-button>
+          >修改2
+          </el-button>
           <el-button
             v-hasPermi="['product:storeProductRule:remove']"
             size="mini"
             type="text"
             icon="el-icon-delete"
             @click="handleDelete(row)"
-          >删除</el-button>
+          >删除
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -118,7 +123,7 @@
       append-to-body
       custom-class="store-product-dialog"
     >
-      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+      <el-form v-if="open" ref="form" :model="form" :rules="rules" label-width="100px">
         <el-row :gutter="24">
           <!-- 商品信息-->
           <el-col :span="8">
@@ -128,20 +133,25 @@
           </el-col>
           <el-col :span="8">
             <el-form-item label="商品分类" prop="cateId">
-              <TreeSelect v-model="form.cateId" :options="options" placeholder="选择商品分类" no-options-text="没有数据" clearable />
+              <TreeSelect
+                v-model="form.cateId"
+                :options="options"
+                placeholder="选择商品分类"
+                no-options-text="没有数据"
+                clearable
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item label="商品关键字" prop="keyword">
+            <el-form-item label="商品关键字" prop="computedKeywordArray">
               <el-select
-                v-model="form.keywordArray"
+                v-model="form.computedKeywordArray"
                 multiple
                 filterable
                 allow-create
                 default-first-option
                 placeholder="请选择或输入商品关键字"
                 style="width: 100%"
-                @change="handleKeywordChange"
               >
                 <el-option
                   v-for="item in keywordOptions"
@@ -192,7 +202,7 @@
         <el-row>
           <el-col :span="24">
             <el-form-item label="商品规格" props="specType">
-              <el-radio-group :disabled="title==='修改商品'" v-model="form.specType" @change="onSpecTypeChange">
+              <el-radio-group v-model="form.specType" :disabled="title==='修改商品'" @change="onSpecTypeChange">
                 <el-radio :label="0">单规格</el-radio>
                 <el-radio :label="1">多规格</el-radio>
               </el-radio-group>
@@ -370,17 +380,17 @@
         <el-row>
           <el-col :span="8">
             <el-form-item label="虚拟销量" prop="ficti">
-              <el-input-number v-model="form.ficti" :precision="0"  :min="0" placeholder="请输入虚拟销量" />
+              <el-input-number v-model="form.ficti" :precision="0" :min="0" placeholder="请输入虚拟销量" />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="购买返回积分" prop="giveIntegral">
-              <el-input-number v-model="form.giveIntegral" :precision="0"  :min="0" placeholder="请输入积分" />
+              <el-input-number v-model="form.giveIntegral" :precision="0" :min="0" placeholder="请输入积分" />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="排序">
-              <el-input-number v-model="form.sort" :precision="0"  :min="0" placeholder="请输入排序" />
+              <el-input-number v-model="form.sort" :precision="0" :min="0" placeholder="请输入排序" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -538,14 +548,21 @@
         :auto-upload="false"
         drag
       >
-        <i class="el-icon-upload"></i>
+        <i class="el-icon-upload" />
         <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
-        <div class="el-upload__tip text-center" slot="tip">
-          <div class="el-upload__tip" slot="tip">
-            <el-checkbox v-model="upload.updateSupport" /> 是否更新已经存在的商品数据
+        <div slot="tip" class="el-upload__tip text-center">
+          <div slot="tip" class="el-upload__tip">
+            <el-checkbox v-model="upload.updateSupport" />
+            是否更新已经存在的商品数据
           </div>
           <span>仅允许导入xls、xlsx格式文件。</span>
-          <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
+          <el-link
+            type="primary"
+            :underline="false"
+            style="font-size:12px;vertical-align: baseline;"
+            @click="importTemplate"
+          >下载模板
+          </el-link>
         </div>
       </el-upload>
       <div slot="footer" class="dialog-footer">
@@ -557,7 +574,16 @@
 </template>
 
 <script>
-import { listStoreProduct, getStoreProduct, delStoreProduct, addStoreProduct, updateStoreProduct, isFormatAttr, onsale, batchAuthorizeGoods } from '@/api/product/storeProduct'
+import {
+  listStoreProduct,
+  getStoreProduct,
+  delStoreProduct,
+  addStoreProduct,
+  updateStoreProduct,
+  isFormatAttr,
+  onsale,
+  batchAuthorizeGoods
+} from '@/api/product/storeProduct'
 import { listStoreCategoryTree } from '@/api/product/storeCategory'
 import { listAllStorage } from '@/api/storage/storage'
 import getStringOSSURL from '@/mixin/getStringOSSURL'
@@ -595,7 +621,29 @@ export default {
         isShow: undefined
       },
       // 表单参数
-      form: {},
+      form: {
+        storeName: undefined,
+        cateId: undefined,
+        unitName: undefined,
+        storeInfo: undefined,
+        keyword: undefined,
+        sliderImage: [],
+        specType: 0,
+        isIntegral: 0,
+        selectRule: undefined,
+        tempId: undefined,
+        description: undefined,
+        ficti: undefined,
+        giveIntegral: undefined,
+        sort: undefined,
+        isSub: 0,
+        isShow: 1,
+        isHot: 1,
+        isBenefit: 1,
+        isBest: 1,
+        isNew: 1,
+        header: []
+      },
       // 关键字选项数据
       keywordOptions: [],
       // 表单校验
@@ -606,7 +654,7 @@ export default {
         cateId: [
           { required: true, message: '请选择商品分类', trigger: 'change' }
         ],
-        keyword: [
+        computedKeywordArray: [
           { required: true, message: '请选择商品关键字', trigger: 'change' }
         ],
         unitName: [
@@ -643,15 +691,15 @@ export default {
         // 是否显示弹出层(用户导入)
         open: false,
         // 弹出层标题(用户导入)
-        title: "",
+        title: '',
         // 是否禁用上传
         isUploading: false,
         // 是否更新已经存在的用户数据
         updateSupport: 0,
         // 设置上传的请求头部
-        headers: { Authorization: "Bearer " + getToken() },
+        headers: { Authorization: 'Bearer ' + getToken() },
         // 上传的地址
-        url: process.env.VUE_APP_BASE_API + "/product/storeProduct/importData"
+        url: process.env.VUE_APP_BASE_API + '/product/storeProduct/importData'
       }
     }
   },
@@ -665,6 +713,7 @@ export default {
     }
   },
   created() {
+    this.reset()
     this.listStoreCategoryTree()
     this.getList()
     this.loadKeywordOptions()
@@ -672,40 +721,34 @@ export default {
   methods: {
     /** 加载关键字选项数据 */
     loadKeywordOptions() {
-      this.getDicts("sys_store_keyword").then(response => {
+      this.getDicts('sys_store_keyword').then(response => {
         this.keywordOptions = response.data
       })
     },
-    /** 处理关键字变化 */
-    handleKeywordChange(value) {
-      // 将选中的关键字数组转换为逗号分隔的字符串
-      this.form.keyword = value.join(',')
-    },
     /** 导入按钮操作 */
     handleImport() {
-      this.upload.title = "商品导入";
-      this.upload.open = true;
+      this.upload.title = '商品导入'
+      this.upload.open = true
     },
     /** 下载模板操作 */
     importTemplate() {
-      this.download('product/storeProduct/template', {
-      }, `product_template_${new Date().getTime()}.xlsx`)
+      this.download('product/storeProduct/template', {}, `product_template_${new Date().getTime()}.xlsx`)
     },
     // 文件上传中处理
     handleFileUploadProgress(event, file, fileList) {
-      this.upload.isUploading = true;
+      this.upload.isUploading = true
     },
     // 文件上传成功处理
     handleFileSuccess(response, file, fileList) {
-      this.upload.open = false;
-      this.upload.isUploading = false;
-      this.$refs.upload.clearFiles();
-      this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
-      this.getList();
+      this.upload.open = false
+      this.upload.isUploading = false
+      this.$refs.upload.clearFiles()
+      this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + '</div>', '导入结果', { dangerouslyUseHTMLString: true })
+      this.getList()
     },
     // 提交上传文件
     submitFileForm() {
-      this.$refs.upload.submit();
+      this.$refs.upload.submit()
     },
     scrollToTop() {
       this.$refs.dialog.$el.getElementsByClassName('el-dialog__body')[0].scrollTop = 0
@@ -766,6 +809,32 @@ export default {
         isNew: 1,
         header: []
       }
+
+      // 在form对象上定义计算属性computedKeywordArray
+      const that = this
+      Object.defineProperty(this.form, 'computedKeywordArray', {
+        get() {
+          // 确保keywordArray存在且是数组
+          if (!this.keywordArray) {
+            that.$set(this, 'keywordArray', [])
+          }
+          return this.keywordArray
+        },
+        set(value) {
+          // 使用$set确保响应式
+          that.$set(this, 'keywordArray', value)
+          // 同步更新keyword用于后端提交
+          this.keyword = value.join(',')
+          // 手动触发表单校验
+          that.$nextTick(() => {
+            if (that.$refs.form) {
+              that.$refs.form.validateField('computedKeywordArray')
+            }
+          })
+        },
+        enumerable: true,
+        configurable: true
+      })
       this.attrs = []
       this.attr = [
         {
@@ -818,16 +887,60 @@ export default {
       this.loading = true
       this.reset()
       const id = row.id || this.ids
+      // 确保关键字选项已加载
+      if (this.keywordOptions.length === 0) {
+        this.loadKeywordOptions()
+      }
       getStoreProduct(id).then(({ data }) => {
         const { ruleList, productInfo } = data
         this.loading = false
         this.form = productInfo
         this.form.header = []
-        // 将关键字字符串转换为数组
+
+        // 为编辑的form对象也添加计算属性
+        const that = this
+        Object.defineProperty(this.form, 'computedKeywordArray', {
+          get() {
+            // 确保keywordArray存在且是数组
+            if (!this.keywordArray) {
+              that.$set(this, 'keywordArray', [])
+            }
+            return this.keywordArray
+          },
+          set(value) {
+            // 使用$set确保响应式
+            that.$set(this, 'keywordArray', value)
+            // 同步更新keyword用于后端提交
+            this.keyword = value.join(',')
+            // 手动触发表单校验
+            that.$nextTick(() => {
+              if (that.$refs.form) {
+                that.$refs.form.validateField('computedKeywordArray')
+              }
+            })
+          },
+          enumerable: true,
+          configurable: true
+        })
+        // 将关键字字符串转换为数组,使用$set确保响应式
         if (this.form.keyword) {
-          this.form.keywordArray = this.form.keyword.split(',').filter(item => item.trim())
+          let keywordArray = this.form.keyword.split(',').filter(item => item.trim())
+
+          // 数据类型转换:确保keywordArray中的值与keywordOptions中的dictValue类型一致
+          if (this.keywordOptions && this.keywordOptions.length > 0) {
+            keywordArray = keywordArray.map(keyword => {
+              const option = this.keywordOptions.find(opt => opt.dictValue == keyword)
+              if (option) {
+                return option.dictValue // 使用原始的dictValue,保持类型一致
+              }
+              return keyword
+            })
+          }
+
+          // 使用$set确保响应式
+          this.$set(this.form, 'keywordArray', keywordArray)
         } else {
-          this.form.keywordArray = []
+          this.$set(this.form, 'keywordArray', [])
         }
         this.ruleList = ruleList
         this.attr = [productInfo.attr]
@@ -1007,7 +1120,7 @@ export default {
   }
 }
 </script>
-<style  lang="scss" scoped>
+<style lang="scss" scoped>
 ::v-deep .vue-treeselect__control {
   height: 31px;
   line-height: 31px;

+ 214 - 155
kxmall-admin-ui/src/views/rider/rider/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+    <el-form v-show="showSearch" ref="queryForm" :model="queryParams" size="small" :inline="true" label-width="68px">
 
       <el-form-item prop="storageId">
         <el-select v-model="queryParams.storageId" placeholder="请选择前置仓" clearable>
@@ -25,31 +25,31 @@
     <el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
+          v-hasPermi="['rider:rider:add']"
           type="primary"
           plain
           icon="el-icon-plus"
           size="mini"
           @click="handleAdd"
-          v-hasPermi="['rider:rider:add']"
         >新增</el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
+          v-hasPermi="['rider:rider:remove']"
           type="danger"
           plain
           icon="el-icon-delete"
           size="mini"
           :disabled="multiple"
           @click="handleDelete"
-          v-hasPermi="['rider:rider:remove']"
         >删除</el-button>
       </el-col>
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+      <right-toolbar :show-search.sync="showSearch" @queryTable="getList" />
     </el-row>
 
     <el-table v-loading="loading" :data="riderList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="配送员编号" align="center" prop="id" v-if="true"/>
+      <el-table-column v-if="true" label="配送员编号" align="center" prop="id" />
       <el-table-column label="配送员名字" align="center" prop="name" />
       <el-table-column label="联系电话" align="center" prop="phone" />
       <el-table-column label="等级" align="center" prop="level">
@@ -61,10 +61,11 @@
       <el-table-column label="擅长类型" align="center" prop="workTypeKeywords" show-overflow-tooltip />
 
       <el-table-column
-          prop="storageId"
-          align="center"
-          show-overflow-tooltip
-          label="配送仓库">
+        prop="storageId"
+        align="center"
+        show-overflow-tooltip
+        label="配送仓库"
+      >
         <template slot-scope="scope">
           {{ storageMap[scope.row.storageId] }}
         </template>
@@ -82,29 +83,30 @@
         </template>
       </el-table-column>
 
-
       <el-table-column
-          align="center"
-          width="160"
-          label="配送时间">
+        align="center"
+        width="160"
+        label="配送时间"
+      >
         <template slot-scope="scope">
           {{ scope.row.deliveryStart }}  ~ {{ scope.row.deliveryEnd }}
         </template>
       </el-table-column>
 
-
       <el-table-column
-          align="center"
-          width="160"
-          label="创建时间">
+        align="center"
+        width="160"
+        label="创建时间"
+      >
         <template slot-scope="scope">
           {{ scope.row.createTime }}
         </template>
       </el-table-column>
       <el-table-column
-          align="center"
-          width="160"
-          label="修改时间">
+        align="center"
+        width="160"
+        label="修改时间"
+      >
         <template slot-scope="scope">
           {{ scope.row.updateTime }}
         </template>
@@ -116,7 +118,7 @@
           <el-button v-show="scope.row.state === 0" type="text" size="mini" style="color: #409EFF;" @click="onOpen(scope.row)">启用</el-button>
           <el-button v-show="scope.row.state === 1 && scope.row.workState === 1" type="text" size="mini" style="color: #409EFF;" @click="onRest(scope.row)">休息</el-button>
           <el-button v-show="scope.row.state === 1 && scope.row.workState === 0" type="text" size="mini" style="color: #409EFF;" @click="onWork(scope.row)">开工</el-button>
-          <el-button size="mini" type="text" style="color: #409EFF;" @click="handleUpdate(scope.row)" v-hasPermi="['rider:rider:edit']">修改</el-button>
+          <el-button v-hasPermi="['rider:rider:edit']" size="mini" type="text" style="color: #409EFF;" @click="handleUpdate(scope.row)">修改</el-button>
           <el-button size="mini" type="text" style="color: #E6A23C;" @click="onResetPassword(scope.row)">重置密码</el-button>
         </template>
       </el-table-column>
@@ -165,14 +167,13 @@
           />
         </el-form-item>
 
-        <el-form-item label="擅长类型" prop="workTypeKeywords">
+        <el-form-item label="擅长类型" prop="computedWorkTypeKeywords">
           <el-select
-            v-model="form.workTypeKeywordsArray"
+            v-model="form.computedWorkTypeKeywords"
             multiple
             filterable
             placeholder="请选择擅长的服务类型"
             style="width: 100%"
-            @change="handleWorkTypeKeywordsChange"
           >
             <el-option
               v-for="item in specialtyOptions"
@@ -184,40 +185,41 @@
         </el-form-item>
 
         <el-form-item prop="phone" label="新密码">
-          <el-input v-model="form.newPassword" autocomplete="off" clearable></el-input>
+          <el-input v-model="form.newPassword" autocomplete="off" clearable />
         </el-form-item>
         <el-form-item prop="name" label="配送周期">
           <el-checkbox-group v-model="form.weekNumberIds" @change="onChanged">
-            <el-checkbox label="周一"></el-checkbox>
-            <el-checkbox label="周二"></el-checkbox>
-            <el-checkbox label="周三"></el-checkbox>
-            <el-checkbox label="周四"></el-checkbox>
-            <el-checkbox label="周五"></el-checkbox>
-            <el-checkbox label="周六"></el-checkbox>
-            <el-checkbox label="周日"></el-checkbox>
+            <el-checkbox label="周一" />
+            <el-checkbox label="周二" />
+            <el-checkbox label="周三" />
+            <el-checkbox label="周四" />
+            <el-checkbox label="周五" />
+            <el-checkbox label="周六" />
+            <el-checkbox label="周日" />
           </el-checkbox-group>
         </el-form-item>
         <el-form-item prop="date" label="配送时间">
           <div class="dateList">
             <el-time-select
-                v-model="form.deliveryStart"
-                :picker-options="{ start: '00:00', step: '00:15', end: '24:00' }"
-                placeholder="起始时间">
-            </el-time-select>
+              v-model="form.deliveryStart"
+              :picker-options="{ start: '00:00', step: '00:15', end: '24:00' }"
+              placeholder="起始时间"
+            />
             <el-time-select
-                v-model="form.deliveryEnd"
-                :picker-options="{ start: '00:00', step: '00:15', end: '24:00'}"
-                placeholder="结束时间">
-            </el-time-select>
+              v-model="form.deliveryEnd"
+              :picker-options="{ start: '00:00', step: '00:15', end: '24:00'}"
+              placeholder="结束时间"
+            />
           </div>
         </el-form-item>
         <el-form-item prop="storageId" label="配送仓库">
           <el-select v-model="form.storageId" placeholder="请选择仓库" clearable filterable>
             <el-option
-                v-for="item in storages"
-                :key="item.id"
-                :label="item.name"
-                :value="item.id"/>
+              v-for="item in storages"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
           </el-select>
         </el-form-item>
 
@@ -244,19 +246,19 @@
             <div class="attachment-list">
               <div v-for="(attachment, index) in form.attachments" :key="index" class="attachment-item">
                 <div class="attachment-preview">
-                  <img v-if="isImageFile(attachment.attachmentUrl)" :src="attachment.attachmentUrl" class="attachment-image" />
+                  <img v-if="isImageFile(attachment.attachmentUrl)" :src="attachment.attachmentUrl" class="attachment-image">
                   <div v-else class="attachment-file">
-                    <i class="el-icon-document"></i>
+                    <i class="el-icon-document" />
                     <span>{{ getFileName(attachment.attachmentUrl) }}</span>
                   </div>
                 </div>
                 <div class="attachment-info">
                   <el-select v-model="attachment.authAttachmentType" placeholder="证件类型" size="mini">
-                    <el-option label="身份证正面" :value="1"></el-option>
-                    <el-option label="身份证反面" :value="2"></el-option>
-                    <el-option label="驾驶证" :value="3"></el-option>
-                    <el-option label="行驶证" :value="4"></el-option>
-                    <el-option label="其他证件" :value="5"></el-option>
+                    <el-option label="身份证正面" :value="1" />
+                    <el-option label="身份证反面" :value="2" />
+                    <el-option label="驾驶证" :value="3" />
+                    <el-option label="行驶证" :value="4" />
+                    <el-option label="其他证件" :value="5" />
                   </el-select>
                   <el-upload
                     :action="uploadUrl"
@@ -269,12 +271,12 @@
                   >
                     <el-button size="mini" type="primary">上传</el-button>
                   </el-upload>
-                  <el-button type="danger" size="mini" @click="removeAttachment(index)" style="margin-left: 10px;">删除</el-button>
+                  <el-button type="danger" size="mini" style="margin-left: 10px;" @click="removeAttachment(index)">删除</el-button>
                 </div>
               </div>
             </div>
 
-            <el-button type="primary" size="mini" @click="addAttachment" style="margin-top: 10px;">添加附件</el-button>
+            <el-button type="primary" size="mini" style="margin-top: 10px;" @click="addAttachment">添加附件</el-button>
           </div>
         </el-form-item>
 
@@ -314,9 +316,8 @@ import { listRider, getRider, delRider, addRider, updateRider,
   updateStateToNomral,
   updateBusinessStateToRest,
   getRiderQrcodeImage,
-  resetRiderPassword} from "@/api/rider/rider";
-import { getAttachmentsByRiderId, deleteAttachment } from "@/api/rider/attachment";
-import {getStorageQrcodeImage, listAllStorage} from "@/api/storage/storage";
+  resetRiderPassword } from '@/api/rider/rider'
+import { listAllStorage } from '@/api/storage/storage'
 
 const num2TextMap = {
   1: '周一',
@@ -338,7 +339,7 @@ const text2NumMap = {
 }
 
 export default {
-  name: "Rider",
+  name: 'Rider',
   components: {
   },
   filters: {
@@ -383,7 +384,7 @@ export default {
       // 配送表格数据
       riderList: [],
       // 弹出层标题
-      title: "",
+      title: '',
       // 是否显示弹出层
       open: false,
       // 查询参数
@@ -404,8 +405,8 @@ export default {
         password: undefined,
         storageId: undefined,
         deliveryRadius: undefined,
-        weekNumberIds:[],
-        newPassword: undefined,
+        weekNumberIds: [],
+        newPassword: undefined
       },
       storages: [],
       qrcode: '',
@@ -418,32 +419,32 @@ export default {
       // 表单校验
       rules: {
         name: [
-          { required: true, message: "配送员名字不能为空", trigger: "blur" }
+          { required: true, message: '配送员名字不能为空', trigger: 'blur' }
         ],
         storageId: [
-          { required: true, message: "所属仓库不能为空", trigger: "blur" }
+          { required: true, message: '所属仓库不能为空', trigger: 'blur' }
         ],
         idCardNumber: [
-          { required: true, message: "身份证号不能为空", trigger: "blur" },
-          { pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/, message: "身份证号格式不正确", trigger: "blur" }
+          { required: true, message: '身份证号不能为空', trigger: 'blur' },
+          { pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/, message: '身份证号格式不正确', trigger: 'blur' }
         ],
         level: [
-          { required: true, message: "请选择骑手等级", trigger: "change" }
+          { required: true, message: '请选择骑手等级', trigger: 'change' }
         ],
         deliveryRadius: [
-          { required: true, message: "服务半径不能为空", trigger: "blur" },
-          { type: 'number', min: 1, max: 50, message: "服务半径必须在1-50km之间", trigger: "blur" }
-        ],
-        workTypeKeywords: [
-          { required: true, message: "请选择擅长类型", trigger: "change" }
+          { required: true, message: '服务半径不能为空', trigger: 'blur' },
+          { type: 'number', min: 1, max: 50, message: '服务半径必须在1-50km之间', trigger: 'blur' }
         ],
+        computedWorkTypeKeywords: [
+          { required: true, message: '请选择擅长类型', trigger: 'change' }
+        ]
       },
       // 上传相关配置
       uploadUrl: process.env.VUE_APP_BASE_API + '/system/oss/upload',
       uploadHeaders: {
         Authorization: 'Bearer ' + this.$store.getters.token
       }
-    };
+    }
   },
   created() {
     this.listAllStorage()
@@ -453,15 +454,10 @@ export default {
   methods: {
     /** 加载擅长类型选项数据 */
     loadSpecialtyOptions() {
-      this.getDicts("sys_store_keyword").then(response => {
+      this.getDicts('sys_store_keyword').then(response => {
         this.specialtyOptions = response.data
       })
     },
-    /** 处理擅长类型变化 */
-    handleWorkTypeKeywordsChange(value) {debugger
-      // 将选中的擅长类型数组转换为逗号分隔的字符串
-      this.form.workTypeKeywords = value.join(',')
-    },
     /** 获取等级文本 */
     getLevelText(level) {
       const levelMap = {
@@ -485,17 +481,17 @@ export default {
     },
     /** 查询配送列表 */
     getList() {
-      this.loading = true;
+      this.loading = true
       listRider(this.queryParams).then(response => {
-        this.riderList = response.rows;
-        this.total = response.total;
-        this.loading = false;
-      });
+        this.riderList = response.rows
+        this.total = response.total
+        this.loading = false
+      })
     },
     // 取消按钮
     cancel() {
-      this.open = false;
-      this.reset();
+      this.open = false
+      this.reset()
     },
     // 表单重置
     reset() {
@@ -525,54 +521,117 @@ export default {
         // 新增字段
         idCardNumber: undefined,
         level: undefined,
-        deliveryRadius: undefined,
         workTypeKeywords: undefined,
-        workTypeKeywordsArray: []
-      };
-      this.resetForm("form");
-      // 重置行上传无需额外状态
+        workTypeKeywordArray: []
+      }
+      // 在form对象上定义计算属性computedWorkTypeKeywords
+      const that = this
+      Object.defineProperty(this.form, 'computedWorkTypeKeywords', {
+        get() {
+          // 确保workTypeKeywordArray存在且是数组
+          if (!this.workTypeKeywordArray) {
+            that.$set(this, 'workTypeKeywordArray', [])
+          }
+          return this.workTypeKeywordArray
+        },
+        set(value) {
+          // 使用$set确保响应式
+          that.$set(this, 'workTypeKeywordArray', value)
+          // 同步更新workTypeKeywords用于后端提交
+          this.workTypeKeywords = value.join(',')
+          // 手动触发表单校验
+          that.$nextTick(() => {
+            if (that.$refs.form) {
+              that.$refs.form.validateField('computedWorkTypeKeywords')
+            }
+          })
+        },
+        enumerable: true,
+        configurable: true
+      })
+      this.resetForm('form')
     },
     /** 搜索按钮操作 */
     handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
+      this.queryParams.pageNum = 1
+      this.getList()
     },
     /** 重置按钮操作 */
     resetQuery() {
-      this.resetForm("queryForm");
-      this.handleQuery();
+      this.resetForm('queryForm')
+      this.handleQuery()
     },
     // 多选框选中数据
     handleSelectionChange(selection) {
       this.ids = selection.map(item => item.id)
-      this.single = selection.length!==1
+      this.single = selection.length !== 1
       this.multiple = !selection.length
     },
     /** 新增按钮操作 */
     handleAdd() {
-      this.reset();
+      this.reset()
       // 设置默认仓库为第一个
       if (this.storages && this.storages.length > 0) {
-        this.form.storageId = this.storages[0].id;
+        this.form.storageId = this.storages[0].id
       }
-      this.open = true;
-      this.title = "添加配送";
+      this.open = true
+      this.title = '添加配送'
     },
     /** 修改按钮操作 */
     async handleUpdate(row) {
-      this.loading = true;
-      this.reset();
+      this.loading = true
+      this.reset()
       const id = row.id || this.ids
       await getRider(id).then(response => {
-        this.loading = false;
-        this.form = response.data;
+        this.loading = false
+        this.form = response.data
         this.form.weekNumberIds = (this.form.weekNumberIds || []).map(item => num2TextMap[item])
 
-        // 处理擅长类型:将字符串转换为数组
+        // 为编辑的form对象也添加计算属性
+        const that = this
+        Object.defineProperty(this.form, 'computedWorkTypeKeywords', {
+          get() {
+            // 确保workTypeKeywordArray存在且是数组
+            if (!this.workTypeKeywordArray) {
+              that.$set(this, 'workTypeKeywordArray', [])
+            }
+            return this.workTypeKeywordArray
+          },
+          set(value) {
+            // 使用$set确保响应式
+            that.$set(this, 'workTypeKeywordArray', value)
+            // 同步更新workTypeKeywords用于后端提交
+            this.workTypeKeywords = value.join(',')
+            // 手动触发表单校验
+            that.$nextTick(() => {
+              if (that.$refs.form) {
+                that.$refs.form.validateField('computedWorkTypeKeywords')
+              }
+            })
+          },
+          enumerable: true,
+          configurable: true
+        })
+
+        // 将关键字字符串转换为数组,使用$set确保响应式
         if (this.form.workTypeKeywords) {
-          this.form.workTypeKeywordsArray = this.form.workTypeKeywords.split(',').filter(item => item.trim())
+          let keywordArray = this.form.workTypeKeywords.split(',').filter(item => item.trim())
+
+          // 数据类型转换:确保keywordArray中的值与specialtyOptions中的dictValue类型一致
+          if (this.specialtyOptions && this.specialtyOptions.length > 0) {
+            keywordArray = keywordArray.map(keyword => {
+              const option = this.specialtyOptions.find(opt => opt.dictValue == keyword)
+              if (option) {
+                return option.dictValue // 使用原始的dictValue,保持类型一致
+              }
+              return keyword
+            })
+          }
+
+          // 使用$set确保响应式
+          this.$set(this.form, 'workTypeKeywordArray', keywordArray)
         } else {
-          this.form.workTypeKeywordsArray = []
+          this.$set(this.form, 'workTypeKeywordArray', [])
         }
 
         // 加载附件数据
@@ -580,74 +639,74 @@ export default {
           this.form.attachments = this.form.attachments.map(att => ({
             ...att,
             sortNo: att.sortNo || 0
-          }));
+          }))
         } else {
-          this.form.attachments = [];
+          this.form.attachments = []
         }
 
-        this.open = true;
-        this.title = "修改配送";
+        this.open = true
+        this.title = '修改配送'
         // 行上传无需额外状态
-      });
-      const img= (await getRiderQrcodeImage({riderId: id})).data
+      })
+      const img = (await getRiderQrcodeImage({ riderId: id })).data
       this.qrcode = 'data:image/gif;base64,' + img
     },
     // 显示银行信息
     async showBankInfo(row) {
       try {
-        this.loading = true;
-        const response = await riderBankInfo(row.id);
+        this.loading = true
+        const response = await riderBankInfo(row.id)
         if (response.code === 200) {
-          this.bankInfo = response.data;
-          this.bankInfoDialogVisible = true;
+          this.bankInfo = response.data
+          this.bankInfoDialogVisible = true
         }
       } catch (error) {
-        console.error('获取银行信息失败:', error);
-        this.$modal.msgError("获取银行信息失败");
+        console.error('获取银行信息失败:', error)
+        this.$modal.msgError('获取银行信息失败')
       } finally {
-        this.loading = false;
+        this.loading = false
       }
     },
     /** 提交按钮 */
     submitForm() {
-      this.$refs["form"].validate(valid => {
+      this.$refs['form'].validate(valid => {
         if (valid) {
-          this.buttonLoading = true;
+          this.buttonLoading = true
           this.form.weekNumberIds = this.form.weekNumberIds.map(date => (text2NumMap[date]))
           if (this.form.id != null) {
             updateRider(this.form).then(response => {
-              this.$modal.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
+              this.$modal.msgSuccess('修改成功')
+              this.open = false
+              this.getList()
             }).finally(() => {
-              this.buttonLoading = false;
-            });
+              this.buttonLoading = false
+            })
           } else {
             addRider(this.form).then(response => {
-              this.$modal.msgSuccess("新增成功");
-              this.open = false;
-              this.getList();
+              this.$modal.msgSuccess('新增成功')
+              this.open = false
+              this.getList()
             }).finally(() => {
-              this.buttonLoading = false;
-            });
+              this.buttonLoading = false
+            })
           }
         }
-      });
+      })
     },
     /** 删除按钮操作 */
     handleDelete(row) {
-      const ids = row.id || this.ids;
+      const ids = row.id || this.ids
       this.$modal.confirm('是否确认删除配送编号为"' + ids + '"的数据项?').then(() => {
-        this.loading = true;
-        return delRider(ids);
+        this.loading = true
+        return delRider(ids)
       }).then(() => {
-        this.loading = false;
-        this.getList();
-        this.$modal.msgSuccess("删除成功");
+        this.loading = false
+        this.getList()
+        this.$modal.msgSuccess('删除成功')
       }).catch(() => {
       }).finally(() => {
-        this.loading = false;
-      });
+        this.loading = false
+      })
     },
     async onDisable(rider) {
       await updateStateToAbort([rider.id])
@@ -691,52 +750,52 @@ export default {
         riderId: null,
         authAttachmentType: 1,
         attachmentUrl: ''
-      });
+      })
     },
 
     // 删除附件
     removeAttachment(index) {
-      this.form.attachments.splice(index, 1);
+      this.form.attachments.splice(index, 1)
       // 无排序字段
     },
 
     // 判断是否为图片文件
     isImageFile(url) {
-      if (!url) return false;
-      const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];
-      return imageExtensions.some(ext => url.toLowerCase().includes(ext));
+      if (!url) return false
+      const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
+      return imageExtensions.some(ext => url.toLowerCase().includes(ext))
     },
 
     // 获取文件名
     getFileName(url) {
-      if (!url) return '';
-      return url.substring(url.lastIndexOf('/') + 1);
+      if (!url) return ''
+      return url.substring(url.lastIndexOf('/') + 1)
     },
 
     // 行上传前校验
     beforeRowUpload(file) {
-      const isLt10M = file.size / 1024 / 1024 < 10;
+      const isLt10M = file.size / 1024 / 1024 < 10
       if (!isLt10M) {
-        this.$message.error('上传文件大小不能超过 10MB!');
-        return false;
+        this.$message.error('上传文件大小不能超过 10MB!')
+        return false
       }
-      return true;
+      return true
     },
     // 行上传成功
     onRowUploadSuccess(res, file, index) {
       if (res && res.code === 200 && res.data && res.data.url) {
-        this.$set(this.form.attachments[index], 'attachmentUrl', res.data.url);
-        this.$message.success('上传成功');
+        this.$set(this.form.attachments[index], 'attachmentUrl', res.data.url)
+        this.$message.success('上传成功')
       } else {
-        this.$message.error(res && res.msg ? res.msg : '上传失败');
+        this.$message.error(res && res.msg ? res.msg : '上传失败')
       }
     },
     // 行上传失败
     onRowUploadError() {
-      this.$message.error('上传失败');
+      this.$message.error('上传失败')
     }
   }
-};
+}
 </script>
 
 <style scoped>

+ 54 - 0
kxmall-app-ui/main.js

@@ -218,6 +218,59 @@ const uploadImg = (num = 9, successCallback, failCallback= undefined) => {
   })
 }
 
+// 新增:直接上传文件路径的方法(不再弹出选择图片对话框)
+const uploadImgByPath = (filePath, successCallback, failCallback = undefined) => {
+  userInfo = uni.getStorageSync('userInfo');
+  var accessToken = userInfo ? userInfo.accessToken : '';
+  let baseUrl = config.def().baseUrl
+  
+  uni.getImageInfo({
+    src: filePath,
+    success: image => {
+      console.log(image)
+      uni.showLoading({
+        title: '图片上传中',
+        mask: true
+      })
+      uni.uploadFile({
+        url: baseUrl + `/oss/app/upload`,
+        file: image,
+        filePath: image.path,
+        header: {
+          Authorization: 'Bearer ' + accessToken,
+        },
+        name: 'file',
+        success: res => {
+          if (successCallback) {
+            successCallback(JSON.parse(res.data).data.url)
+          }
+        },
+        fail: err => {
+          if (failCallback) {
+            failCallback(err)
+          } else {
+            uni.showToast({
+              title: '上传图片失败',
+              icon: 'none',
+              duration: 2000,
+            })
+          }
+        },
+        complete: res => {
+          uni.hideLoading()
+        },
+      })
+    },
+    fail: err => {
+      uni.showToast({
+        title: '获取图片信息失败',
+        icon: 'none',
+        duration: 2000,
+      })
+    },
+  })
+}
+
 function get_suffix(filename) {
   var pos = filename.lastIndexOf('.')
   var suffix = ''
@@ -283,6 +336,7 @@ Vue.prototype.$api = {
   prePage,
   request,
   uploadImg,
+  uploadImgByPath,
   logout,
   isVip,
   setUserInfo,

+ 28 - 12
kxmall-app-ui/pages/order/create.vue

@@ -311,6 +311,14 @@ export default {
     }
   },
   components: {uniPopup},
+  computed: {
+    // 动态计算照片区域高度
+    photoContainerHeight() {
+      const totalItems = this.orderReqeust.photos.length + (this.orderReqeust.photos.length < 5 ? 1 : 0) // 照片数量 + 添加按钮
+      const rows = Math.ceil(totalItems / 2) // 每行2张,计算行数
+      return rows * 200 + 'rpx' // 每行200rpx高度(包含180rpx照片高度 + 20rpx行间距)
+    }
+  },
   onShow() {
     this.isVip = this.$api.isVip()
   },
@@ -433,7 +441,7 @@ export default {
     
     // 上传照片
     uploadPhoto(filePath) {
-      this.$api.uploadImg(filePath, (url) => {
+      this.$api.uploadImgByPath(filePath, (url) => {
         this.orderReqeust.photos.push(url)
         uni.showToast({
           title: '上传成功',
@@ -1211,7 +1219,6 @@ page {
   line-height: 100upx;
   font-size: 28upx;
   clear: both;
-  
 }
 
 .distribution-list-bottom {
@@ -1414,25 +1421,33 @@ page {
 
 /* 照片上传相关样式 */
 .photo-upload-section {
-  flex-direction: column !important;
+  flex-direction: row !important; /* 改为横向布局 */
   align-items: flex-start !important;
+  height: auto !important; /* 覆盖distribution-list的固定高度 */
+  line-height: normal !important; /* 覆盖distribution-list的固定行高 */
+  padding: 30rpx 0 !important; /* 添加上下内边距 */
 }
 
 .photo-upload-container {
-  width: 100%;
-  margin-top: 20rpx;
+  flex: 1; /* 占据剩余空间 */
+  margin-top: 50rpx;
+  margin-left: 20rpx; /* 与文案保持间距 */
+  min-height: 200rpx; /* 确保至少有200rpx高度 */
 }
 
 .photo-list {
   display: flex;
   flex-wrap: wrap;
   gap: 20rpx;
+  align-content: flex-start;
+  width: 100%;
 }
 
 .photo-item {
   position: relative;
-  width: 160rpx;
-  height: 160rpx;
+  width: 120rpx; /* 固定正方形宽度 */
+  height: 120rpx; /* 固定正方形高度 */
+  margin-bottom: 20rpx; /* 行间距 */
 }
 
 .photo-preview {
@@ -1462,8 +1477,9 @@ page {
 }
 
 .photo-add-btn {
-  width: 160rpx;
-  height: 160rpx;
+  width: 120rpx; /* 与photo-item保持一致 */
+  height: 120rpx; /* 与photo-item保持一致 */
+  margin-bottom: 20rpx; /* 与photo-item保持一致的行间距 */
   border: 2rpx dashed #ccc;
   border-radius: 8rpx;
   display: flex;
@@ -1474,13 +1490,13 @@ page {
 }
 
 .photo-add-icon {
-  font-size: 48rpx;
+  font-size: 32rpx; /* 调小图标尺寸 */
   color: #999;
-  margin-bottom: 8rpx;
+  margin-bottom: 4rpx; /* 减少间距 */
 }
 
 .photo-add-text {
-  font-size: 24rpx;
+  font-size: 20rpx; /* 调小文字尺寸 */
   color: #999;
 }