Prechádzať zdrojové kódy

[√]商品,多规格时,单价也要必填,price字段不管单规格还是多规格都要填
[√]商家表增加字段,bonus_rate,默认为0 商家佣金比例0-100%
[√]订单表增加字段:收货人手机号
[×]商家表有月底余额的,之前漏了,那个商家提现你就用balance字段就行
[×]商家入驻统一审核通过创建管理账户
[×]确认收货调我的complete方法,返回数组就是成功,返回false就是失败,获取error提示

罗永浩 3 mesiacov pred
rodič
commit
e52676c4ab

+ 39 - 0
.kiro/settings/mcp.json

@@ -0,0 +1,39 @@
+{
+  "mcpServers": {
+    "windows-mcp": {
+      "command": "uv",
+      "args": [
+        "--directory",
+        "C:/Windows-MCP",
+        "run",
+        "main.py"
+      ],
+      "env": {
+        "PYTHONUNBUFFERED": "1"
+      },
+      "metadata": {
+        "description": "Windows-MCP server for local system operations",
+        "homepage": "https://github.com/CursorTouch/Windows-MCP"
+      }
+    },
+    "filesystem": {
+      "command": "C:\\Users\\Administrator\\AppData\\Roaming\\nvm\\v18.20.2\\node.exe",
+      "args": [
+        "C:\\Users\\Administrator\\AppData\\Roaming\\nvm\\v18.20.2\\node_modules\\@modelcontextprotocol\\server-filesystem\\dist\\index.js",
+        "D:\\www"
+      ]
+    },
+    "fetch": {
+      "command": "C:\\Users\\Administrator\\AppData\\Roaming\\nvm\\v20.19.5\\node.exe",
+      "args": [
+        "C:\\Users\\Administrator\\AppData\\Roaming\\nvm\\v20.19.5\\node_modules\\fetch-mcp\\dist\\index.js"
+      ]
+    },
+    "windows-cli": {
+      "command": "C:\\Users\\Administrator\\AppData\\Roaming\\nvm\\v20.19.5\\node.exe",
+      "args": [
+        "C:\\Users\\Administrator\\AppData\\Roaming\\nvm\\v20.19.5\\windows-cli-mcp-server-main\\dist\\index.js"
+      ]
+    }
+  }
+}

+ 9 - 8
addons/admin/src/views/goods/goods.vue

@@ -63,7 +63,8 @@
 				<el-table-column label="审核状态" width="150" align="center">
 					<template slot-scope="{row}">
 						<!-- 已发布和待发布可以互转 -->
-						<el-switch v-if="row.status === 1 || row.status === 2" v-model="row.status" @change="editStatus(row)" :active-value="1" :inactive-value="2" active-color="#13ce66" inactive-color="#ff4949" />
+						<el-switch v-if="row.status === 1 || row.status === 2" v-model="row.status" @change="editStatus(row)"
+							:active-value="1" :inactive-value="2" active-color="#13ce66" inactive-color="#ff4949" />
 						<!-- 其他状态显示文本 -->
 						<span v-else>{{ row.status_text }}</span>
 					</template>
@@ -164,15 +165,15 @@
 						:closable="false" style="margin-top: 10px;" />
 				</el-form-item>
 
+				<el-form-item label="商品价格" prop="price">
+					<el-input-number v-model="editForm.price" :precision="2" :step="0.1" :min="0" placeholder="请输入售价"
+						style="width: 100%;" controls-position="right">
+						<template slot="prepend">¥</template>
+					</el-input-number>
+				</el-form-item>
+
 				<!-- 单规格 -->
 				<template v-if="editForm.sku_type == 1">
-					<el-form-item label="商品价格" prop="price">
-						<el-input-number v-model="editForm.price" :precision="2" :step="0.1" :min="0" placeholder="请输入售价"
-							style="width: 100%;" controls-position="right">
-							<template slot="prepend">¥</template>
-						</el-input-number>
-					</el-form-item>
-
 					<el-form-item label="商品库存" prop="stock">
 						<el-input-number v-model="editForm.stock" :step="1" :min="0" placeholder="请输入库存" style="width: 100%;"
 							controls-position="right">

+ 26 - 6
addons/admin/src/views/order/order.vue

@@ -69,7 +69,7 @@
 				<el-row :gutter="15">
 					<el-col :md="8" :sm="12">
 						<el-form-item label="关键词:">
-							<el-input v-model="table.where.keyword" placeholder="订单号/商品名称" clearable />
+							<el-input v-model="table.where.keyword" placeholder="订单号/商品名称/收货人手机" clearable />
 						</el-form-item>
 					</el-col>
 					<el-col :md="16" :sm="12">
@@ -110,7 +110,15 @@
 							<span>¥{{ row.total }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column prop="receiver_address" label="收货地址" min-width="200" />
+					<el-table-column prop="receiver_info" label="收货信息" min-width="180">
+						<template slot-scope="{row}">
+							<div style="line-height: 1.5;">
+								<div><strong>{{ row.receiver_name }}</strong></div>
+								<div style="color: #666; font-size: 12px;">{{ row.receiver_mobile || '-' }}</div>
+								<div style="color: #999; font-size: 11px;">{{ row.receiver_area }}</div>
+							</div>
+						</template>
+					</el-table-column>
 					<el-table-column prop="status" label="订单状态" width="160" align="center">
 						<template slot-scope="{row}">
 							<div style="display: flex; flex-direction: column; gap: 5px; align-items: center;">
@@ -154,7 +162,7 @@
 
 							<!-- 已发货:完成 -->
 							<el-link @click="quickComplete(row)" icon="el-icon-circle-check" type="success" :underline="false"
-								v-if="row.status == 3 && permission.includes('sys:order:status')" class="ele-action">完成</el-link>
+								v-if="row.status == 3 && permission.includes('sys:order:status')" class="ele-action">确认收货</el-link>
 
 							<!-- 删除 -->
 							<el-popconfirm title="确定要删除此订单吗?" @confirm="remove(row)" class="ele-action">
@@ -300,7 +308,19 @@
 								<span class="info-value">{{ orderInfo.receiver_name }}</span>
 							</div>
 						</el-col>
-						<el-col :span="16">
+						<el-col :span="8">
+							<div class="info-item">
+								<span class="info-label">收货手机:</span>
+								<span class="info-value">{{ orderInfo.receiver_mobile || '-' }}</span>
+							</div>
+						</el-col>
+						<el-col :span="8">
+							<div class="info-item">
+								<span class="info-label">商品数量:</span>
+								<span class="info-value">{{ orderInfo.num || 0 }}</span>
+							</div>
+						</el-col>
+						<el-col :span="24">
 							<div class="info-item">
 								<span class="info-label">所在地区:</span>
 								<span class="info-value">{{ orderInfo.receiver_area }}</span>
@@ -654,7 +674,7 @@ export default {
 		},
 		// 确认完成
 		handleComplete() {
-			this.$confirm('确定要将此订单标记为已完成吗?', '提示', {
+			this.$confirm('确定要确认收货吗?', '提示', {
 				type: 'warning'
 			}).then(() => {
 				const loading = this.$loading({ lock: true });
@@ -741,7 +761,7 @@ export default {
 		},
 		// 快捷完成订单
 		quickComplete(row) {
-			this.$confirm('确定要将此订单标记为已完成吗?', '提示', {
+			this.$confirm('确定要确认收货吗?', '提示', {
 				type: 'warning'
 			}).then(() => {
 				const loading = this.$loading({ lock: true });

+ 699 - 81
addons/admin/src/views/store/store.vue

@@ -57,10 +57,12 @@
 					</el-col>
 					<el-col :md="12" :sm="12">
 						<div class="ele-form-actions">
-							<el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search"
-								class="ele-btn-icon">查询</el-button>
-							<el-button @click="handleReset">重置</el-button>
-						</div>
+						<el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search"
+						class="ele-btn-icon">查询</el-button>
+						<el-button @click="handleReset">重置</el-button>
+						 <el-button type="success" @click="showAdd" icon="el-icon-plus"
+									v-if="permission.includes('sys:store:add')">新增商家</el-button>
+							</div>
 					</el-col>
 				</el-row>
 			</el-form>
@@ -82,6 +84,11 @@
 					<el-table-column prop="category_name" label="商家分类" min-width="120" show-overflow-tooltip />
 					<el-table-column prop="real_name" label="联系姓名" min-width="120" show-overflow-tooltip />
 					<el-table-column prop="phone" label="电话" min-width="130" show-overflow-tooltip />
+					<el-table-column prop="bonus_rate" label="佣金比例" min-width="100" align="center">
+						<template slot-scope="{row}">
+							<span>{{ row.bonus_rate || 0 }}%</span>
+						</template>
+					</el-table-column>
 					<el-table-column prop="order_count" label="累计订单销量" min-width="130" show-overflow-tooltip />
 					<el-table-column prop="order_total" label="累计销售额" min-width="130" show-overflow-tooltip />
 					<el-table-column prop="create_time" label="入驻时间" min-width="160" show-overflow-tooltip />
@@ -108,6 +115,8 @@
 						<template slot-scope="{row}">
 							<el-link @click="showInfo(row)" icon="el-icon-view" type="primary" :underline="false"
 								v-if="permission.includes('store:store:index')">详情</el-link>
+							<el-link @click="showEdit(row)" icon="el-icon-edit" type="warning" :underline="false"
+								v-if="permission.includes('sys:store:edit')">编辑</el-link>
 							<el-link @click="showConfirm(row)" icon="el-icon-check" type="success" :underline="false"
 								v-if="row.status == STORE_STATUS.PENDING && permission.includes('sys:store:edit')">审核</el-link>
 							<el-link @click="setStatus(row)" icon="el-icon-switch-button"
@@ -125,78 +134,120 @@
 			</ele-data-table>
 		</el-card>
 
-		<!-- 详情弹窗 -->
-		<el-dialog title="商家详情" :visible.sync="showInfoDialog" width="800px" @closed="infoForm = {}"
-			:destroy-on-close="true" top="20px" :close-on-click-modal="false">
-			<el-form :model="infoForm" label-width="120px" v-if="infoForm.id">
-				<el-row :gutter="15">
-					<el-col :sm="12">
-						<el-form-item label="商家ID:">
-							<span>{{ infoForm.id }}</span>
-						</el-form-item>
-					</el-col>
-					<el-col :sm="12">
-						<el-form-item label="店铺名称:">
-							<span>{{ infoForm.name }}</span>
-						</el-form-item>
-					</el-col>
-					<el-col :sm="12">
-						<el-form-item label="联系姓名:">
-							<span>{{ infoForm.real_name }}</span>
-						</el-form-item>
-					</el-col>
-					<el-col :sm="12">
-						<el-form-item label="联系电话:">
-							<span>{{ infoForm.phone }}</span>
-						</el-form-item>
-					</el-col>
-					<el-col :sm="12">
-						<el-form-item label="商家分类:">
-							<span>{{ infoForm.category_name || '未分类' }}</span>
-						</el-form-item>
-					</el-col>
-					<el-col :sm="24">
-						<el-form-item label="所在地址:">
-							<span>{{ infoForm.address || '未填写' }}</span>
-						</el-form-item>
-					</el-col>
-					<el-col :sm="24" v-if="infoForm.logo">
-						<el-form-item label="门头照:">
-							<el-image :src="infoForm.logo" style="width: 200px; height: auto;"
-								:preview-src-list="[infoForm.logo]" />
-						</el-form-item>
-					</el-col>
-					<el-col :sm="12">
-						<el-form-item label="入驻时间:">
-							<span>{{ infoForm.create_time }}</span>
-						</el-form-item>
-					</el-col>
-					<el-col :sm="12">
-						<el-form-item label="商家状态:">
-							<ele-dot :type="getStatusType(infoForm.status)" :text="getStatusText(infoForm.status)" />
-						</el-form-item>
-					</el-col>
-					<el-col :sm="24" v-if="infoForm.business_license">
-						<el-form-item label="营业执照:">
-							<el-image :src="infoForm.business_license" style="width: 200px; height: auto;"
-								:preview-src-list="[infoForm.business_license]" />
-						</el-form-item>
-					</el-col>
-					<el-col :sm="24" v-if="infoForm.other_photo">
-						<el-form-item label="其他证件照:">
-							<el-image :src="infoForm.other_photo" style="width: 200px; height: auto;"
-								:preview-src-list="[infoForm.other_photo]" />
-						</el-form-item>
-					</el-col>
-					<el-col :sm="24" v-if="infoForm.confirm_remark">
-						<el-form-item label="审核备注:">
-							<span>{{ infoForm.confirm_remark }}</span>
-						</el-form-item>
-					</el-col>
-				</el-row>
-			</el-form>
-			<div slot="footer">
-				<el-button @click="showInfoDialog = false">关闭</el-button>
+		<!-- 通用弹窗(详情/添加/编辑) -->
+		<el-dialog :title="dialogTitle" :visible.sync="showDialog" width="900px" @closed="resetForm"
+			:destroy-on-close="true" top="5vh" :close-on-click-modal="false" class="store-dialog">
+			<div slot="title" class="dialog-header">
+				<i :class="dialogIcon" class="dialog-icon"></i>
+				<span class="dialog-title">{{ dialogTitle }}</span>
+			</div>
+			<div class="dialog-content">
+				<el-form :model="form" ref="storeForm" :rules="formRules" label-width="120px" class="store-form">
+					<el-row :gutter="20">
+						<el-col :sm="12" v-if="dialogMode === 'detail'">
+							<el-form-item label="商家ID:">
+								<span class="detail-text">{{ form.id }}</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="12">
+							<el-form-item label="店铺名称:" prop="name">
+								<el-input v-if="dialogMode !== 'detail'" v-model="form.name" placeholder="请输入店铺名称" size="medium" />
+								<span v-else class="detail-text">{{ form.name }}</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="12">
+							<el-form-item label="联系姓名:" prop="real_name">
+								<el-input v-if="dialogMode !== 'detail'" v-model="form.real_name" placeholder="请输入联系姓名" size="medium" />
+								<span v-else class="detail-text">{{ form.real_name }}</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="12">
+							<el-form-item label="联系电话:" prop="phone">
+								<el-input v-if="dialogMode !== 'detail'" v-model="form.phone" placeholder="请输入联系电话" size="medium" />
+								<span v-else class="detail-text">{{ form.phone }}</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="12">
+							<el-form-item label="商家分类:" prop="category_id">
+								<el-select v-if="dialogMode !== 'detail'" v-model="form.category_id" placeholder="请选择商家分类" clearable filterable size="medium" style="width: 100%">
+									<el-option v-for="item in categoryOptions" :key="item.id" :label="item.name" :value="item.id" />
+								</el-select>
+								<span v-else class="detail-text">{{ form.category_name || '未分类' }}</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="12">
+							<el-form-item label="佣金比例:" prop="bonus_rate">
+								<div v-if="dialogMode !== 'detail'" class="bonus-rate-input">
+									<el-input-number v-model="form.bonus_rate" :min="0" :max="100" :precision="2" placeholder="请输入佣金比例" size="medium" class="rate-number" />
+									<span class="unit-text">%</span>
+								</div>
+								<span v-else class="detail-text bonus-rate">{{ form.bonus_rate || 0 }}%</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="24">
+							<el-form-item label="所在地址:" prop="address">
+								<el-input v-if="dialogMode !== 'detail'" v-model="form.address" placeholder="请输入所在地址" size="medium" />
+								<span v-else class="detail-text">{{ form.address || '未填写' }}</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="24">
+							<el-form-item label="门头照:">
+								<div v-if="dialogMode !== 'detail'" class="upload-section">
+									<upload-image v-model="form.logo" :limit="1" :type="1" @upload-success="handleUploadSuccess" />
+									<div class="upload-tip">
+										<i class="el-icon-info"></i>
+										支持 JPG、PNG 格式,文件大小不超过 10MB
+									</div>
+								</div>
+								<div v-else-if="form.logo" class="image-preview">
+									<el-image :src="form.logo" class="preview-image"
+										:preview-src-list="[form.logo]" />
+								</div>
+								<span v-else class="detail-text empty-text">未上传</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="12" v-if="dialogMode === 'detail'">
+							<el-form-item label="入驻时间:">
+								<span class="detail-text time-text">{{ form.create_time }}</span>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="12" v-if="dialogMode === 'detail'">
+							<el-form-item label="商家状态:">
+								<div class="status-wrapper">
+									<ele-dot :type="getStatusType(form.status)" :text="getStatusText(form.status)" />
+								</div>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="24" v-if="dialogMode === 'detail' && form.business_license">
+							<el-form-item label="营业执照:">
+								<div class="image-preview">
+									<el-image :src="form.business_license" class="preview-image"
+										:preview-src-list="[form.business_license]" />
+								</div>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="24" v-if="dialogMode === 'detail' && form.other_photo">
+							<el-form-item label="其他证件照:">
+								<div class="image-preview">
+									<el-image :src="form.other_photo" class="preview-image"
+										:preview-src-list="[form.other_photo]" />
+								</div>
+							</el-form-item>
+						</el-col>
+						<el-col :sm="24" v-if="dialogMode === 'detail' && form.confirm_remark">
+							<el-form-item label="审核备注:">
+								<div class="remark-text">{{ form.confirm_remark }}</div>
+							</el-form-item>
+						</el-col>
+					</el-row>
+				</el-form>
+			</div>
+			<div slot="footer" class="dialog-footer">
+				<el-button @click="showDialog = false" size="medium">{{ dialogMode === 'detail' ? '关闭' : '取消' }}</el-button>
+				<el-button v-if="dialogMode !== 'detail'" type="primary" @click="saveStore" size="medium" :loading="saveLoading">
+					<i class="el-icon-check" style="margin-right: 5px;"></i>
+					确定
+				</el-button>
 			</div>
 		</el-dialog>
 
@@ -236,9 +287,13 @@ import {
 } from "@/constants/storeConstants";
 
 import { STATUS, STATUS_TYPE } from "@/constants/commonConstants";
+import UploadImage from "@/components/uploadImage.vue";
 
 export default {
 	name: "StoreManage",
+	components: {
+		UploadImage
+	},
 	data() {
 		return {
 			STORE_STATUS,
@@ -287,10 +342,35 @@ export default {
 			},
 			choose: [],
 			categoryOptions: [], // 商家分类选项
-			showInfoDialog: false,
+			// 通用弹窗相关
+			showDialog: false,
+			dialogMode: 'detail', // detail: 详情, add: 添加, edit: 编辑
+			form: {},
+			// 审核弹窗相关
 			showConfirmDialog: false,
-			infoForm: {},
 			confirmForm: {},
+			// 保存加载状态
+			saveLoading: false,
+			// 表单验证规则
+			formRules: {
+				name: [
+					{ required: true, message: '请输入店铺名称', trigger: 'blur' }
+				],
+				real_name: [
+					{ required: true, message: '请输入联系姓名', trigger: 'blur' }
+				],
+				phone: [
+					{ required: true, message: '请输入联系电话', trigger: 'blur' },
+					{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
+				],
+				category_id: [
+					{ required: true, message: '请选择商家分类', trigger: 'change' }
+				],
+				bonus_rate: [
+					{ required: true, message: '请输入佣金比例', trigger: 'blur' },
+					{ type: 'number', min: 0, max: 100, message: '佣金比例必须在0-100之间', trigger: 'blur' }
+				]
+			},
 			confirmRules: {
 				status: [
 					{ required: true, message: '请选择审核结果', trigger: 'change' }
@@ -311,7 +391,25 @@ export default {
 		}
 	},
 	computed: {
-		...mapGetters(["permission"])
+		...mapGetters(["permission"]),
+		// 弹窗标题
+		dialogTitle() {
+			const titleMap = {
+				detail: '商家详情',
+				add: '新增商家',
+				edit: '编辑商家'
+			};
+			return titleMap[this.dialogMode] || '商家信息';
+		},
+		// 弹窗图标
+		dialogIcon() {
+			const iconMap = {
+				detail: 'el-icon-view',
+				add: 'el-icon-plus',
+				edit: 'el-icon-edit'
+			};
+			return iconMap[this.dialogMode] || 'el-icon-info';
+		}
 	},
 	mounted() {
 		// 初始化时,根据 URL 参数或默认值设置状态
@@ -379,14 +477,58 @@ export default {
 			});
 		},
 
+		/* 重置表单 */
+		resetForm() {
+			this.form = {
+				id: null,
+				name: '',
+				real_name: '',
+				phone: '',
+				category_id: null,
+				bonus_rate: 0,
+				address: '', // 确保 address字段始终为空字符串而不是null或undefined
+				logo: ''
+			};
+			if (this.$refs.storeForm) {
+				this.$refs.storeForm.clearValidate();
+			}
+			console.log('表单重置后的数据:', this.form);
+		},
+
+		/* 显示新增 */
+		showAdd() {
+			this.dialogMode = 'add';
+			this.resetForm();
+			this.showDialog = true;
+		},
+
+		/* 显示编辑 */
+		showEdit(row) {
+			const loading = this.$loading({ lock: true });
+			this.$http.get('/store/info', { params: { id: row.id } }).then(res => {
+				loading.close();
+				if (res.data.code === 0) {
+					this.form = res.data.data || {};
+					this.dialogMode = 'edit';
+					this.showDialog = true;
+				} else {
+					this.$message.error(res.data.msg);
+				}
+			}).catch(e => {
+				loading.close();
+				this.$message.error(e.message);
+			});
+		},
+
 		/* 显示详情 */
 		showInfo(row) {
 			const loading = this.$loading({ lock: true });
 			this.$http.get('/store/info', { params: { id: row.id } }).then(res => {
 				loading.close();
 				if (res.data.code === 0) {
-					this.infoForm = res.data.data || {};
-					this.showInfoDialog = true;
+					this.form = res.data.data || {};
+					this.dialogMode = 'detail';
+					this.showDialog = true;
 				} else {
 					this.$message.error(res.data.msg);
 				}
@@ -396,6 +538,49 @@ export default {
 			});
 		},
 
+		/* 上传成功回调 */
+		handleUploadSuccess(data) {
+			this.$message({
+				type: 'success',
+				message: '图片上传成功',
+				duration: 2000
+			});
+			console.log('上传成功:', data);
+		},
+
+		/* 保存商家 */
+		saveStore() {
+			this.$refs['storeForm'].validate((valid) => {
+				if (valid) {
+					this.saveLoading = true;
+					const url = this.dialogMode === 'add' ? '/store/add' : '/store/edit';
+					
+					// 确保所有字段都被包含,包括空字符串
+					const formData = {
+						...this.form,
+						// 确保 address字段始终存在,即使是空字符串
+						address: this.form.address || ''
+					};
+					
+					console.log('提交的表单数据:', formData);
+					
+					this.$http.post(url, formData).then(res => {
+						this.saveLoading = false;
+						if (res.data.code === 0) {
+							this.showDialog = false;
+							this.$message({ type: 'success', message: res.data.msg });
+							this.$refs.table.reload();
+						} else {
+							this.$message.error(res.data.msg);
+						}
+					}).catch(e => {
+						this.saveLoading = false;
+						this.$message.error(e.message);
+					});
+				}
+			});
+		},
+
 		/* 显示审核 */
 		showConfirm(row) {
 			this.confirmForm = {
@@ -560,4 +745,437 @@ export default {
 	color: #909399;
 	font-size: 12px;
 }
+
+/* 弹窗样式优化 */
+.store-dialog {
+	::v-deep .el-dialog {
+		border-radius: 12px;
+		box-shadow: 0 12px 32px 4px rgba(0, 0, 0, 0.04), 0 8px 20px rgba(0, 0, 0, 0.08);
+		overflow: hidden;
+	}
+
+	::v-deep .el-dialog__header {
+		padding: 0;
+		border-bottom: 1px solid #f0f0f0;
+	}
+
+	::v-deep .el-dialog__body {
+		padding: 0;
+		max-height: 70vh;
+		overflow-y: auto;
+	}
+
+	::v-deep .el-dialog__footer {
+		padding: 16px 24px;
+		border-top: 1px solid #f0f0f0;
+		background-color: #fafafa;
+	}
+}
+
+/* 弹窗头部 */
+.dialog-header {
+	display: flex;
+	align-items: center;
+	padding: 20px 24px;
+	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+	color: white;
+
+	.dialog-icon {
+		font-size: 20px;
+		margin-right: 12px;
+		opacity: 0.9;
+	}
+
+	.dialog-title {
+		font-size: 18px;
+		font-weight: 600;
+		letter-spacing: 0.5px;
+	}
+}
+
+/* 弹窗内容 */
+.dialog-content {
+	padding: 32px 24px;
+	background-color: #fff;
+
+	// 为整个表单区域添加间距
+	.el-row {
+		margin-left: -10px !important;
+		margin-right: -10px !important;
+		
+		.el-col {
+			padding-left: 10px !important;
+			padding-right: 10px !important;
+		}
+	}
+}
+
+/* 表单样式 */
+.store-form {
+	// 确保表单项之间有足够的间距
+	::v-deep .el-form-item {
+		margin-bottom: 24px !important;
+		padding-bottom: 8px;
+
+		&:last-child {
+			margin-bottom: 0 !important;
+		}
+
+		.el-form-item__label {
+			font-weight: 500;
+			color: #303133;
+			font-size: 14px;
+			line-height: 1.5;
+			margin-bottom: 8px;
+			height: auto !important;
+		}
+
+		.el-form-item__content {
+			line-height: normal;
+			min-height: 40px;
+			display: flex;
+			align-items: center;
+		}
+
+		.el-input, .el-select {
+			width: 100%;
+			
+			::v-deep .el-input__inner {
+				border-radius: 6px;
+				border: 1.5px solid #e4e7ed;
+				transition: all 0.3s ease;
+				height: 40px;
+				line-height: 40px;
+
+				&:focus {
+					border-color: #409eff;
+					box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
+				}
+			}
+		}
+
+		.el-select {
+			::v-deep .el-input__inner {
+				height: 40px;
+				line-height: 40px;
+			}
+		}
+
+		.el-input-number {
+			width: 100%;
+			
+			&:not(.rate-number) {
+				::v-deep .el-input__inner {
+					border-radius: 6px;
+					border: 1.5px solid #e4e7ed;
+					transition: all 0.3s ease;
+					height: 40px;
+					line-height: 40px;
+
+					&:focus {
+						border-color: #409eff;
+						box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
+					}
+				}
+			}
+		}
+	}
+
+	// 为每个栏目添加间距
+	.el-row {
+		margin-bottom: 0;
+		
+		.el-col {
+			padding-bottom: 0;
+		}
+	}
+}
+
+/* 详情文本样式 */
+.detail-text {
+	color: #606266;
+	font-size: 14px;
+	line-height: 1.6;
+	padding: 10px 12px;
+	min-height: 40px;
+	display: flex;
+	align-items: center;
+	background-color: #f8f9fa;
+	border: 1px solid #e9ecef;
+	border-radius: 6px;
+	margin: 0;
+
+	&.time-text {
+		color: #909399;
+		font-family: 'Monaco', 'Menlo', monospace;
+	}
+
+	&.bonus-rate {
+		color: #e6a23c;
+		font-weight: 600;
+		font-size: 15px;
+		background-color: #fef7e6;
+		border-color: #f4d77a;
+	}
+
+	&.empty-text {
+		color: #c0c4cc;
+		font-style: italic;
+		background-color: #f5f5f5;
+	}
+}
+
+/* 佣金比例输入 */
+.bonus-rate-input {
+	display: flex;
+	align-items: stretch;
+	height: 40px;
+	position: relative;
+
+	.rate-number {
+		flex: 1;
+		margin-right: 0;
+		
+		::v-deep .el-input__inner {
+			border-radius: 6px 0 0 6px !important;
+			border-right: none !important;
+		}
+		
+		::v-deep .el-input-number__increase,
+		::v-deep .el-input-number__decrease {
+			border-radius: 0;
+		}
+		
+		::v-deep .el-input-number__increase {
+			border-radius: 0;
+			border-right: none;
+		}
+	}
+
+	.unit-text {
+		color: #606266;
+		font-size: 14px;
+		font-weight: 500;
+		min-width: 32px;
+		text-align: center;
+		height: 40px;
+		line-height: 40px;
+		background-color: #f5f7fa;
+		border: 1.5px solid #e4e7ed;
+		border-left: none;
+		border-radius: 0 6px 6px 0;
+		padding: 0 8px;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+}
+
+/* 上传区域 */
+.upload-section {
+	padding: 10px 0;
+	min-height: 40px;
+	display: flex;
+	align-items: flex-start;
+
+	// 优化上传组件样式
+	::v-deep .finish_room {
+		width: 100%;
+	}
+
+	::v-deep .finish_room2 {
+		align-items: flex-start;
+	}
+
+	::v-deep .room_add_img {
+		border: 2px dashed #d9d9d9;
+		border-radius: 8px;
+		background-color: #fafafa;
+		transition: all 0.3s ease;
+		margin-left: 0;
+		margin-top: 0;
+
+		&:hover {
+			border-color: #409eff;
+			background-color: #f0f8ff;
+		}
+	}
+
+	::v-deep .room_img {
+		border-radius: 8px;
+		overflow: hidden;
+		margin-left: 0;
+		margin-top: 0;
+		margin-right: 10px;
+		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+
+		.img {
+			border-radius: 8px;
+		}
+	}
+
+	::v-deep .im-button {
+		top: -8px;
+		right: -8px;
+		width: 24px;
+		height: 24px;
+		background-color: #ff4757;
+		border: 2px solid #fff;
+		box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+
+		&:hover {
+			background-color: #ff3742;
+		}
+	}
+
+	::v-deep .im-close,
+	::v-deep .im-close1 {
+		width: 12px;
+		height: 2px;
+		left: 4px;
+		bottom: 9px;
+	}
+
+	.upload-tip {
+		margin-top: 8px;
+		color: #909399;
+		font-size: 12px;
+		line-height: 1.5;
+		display: flex;
+		align-items: center;
+		gap: 4px;
+
+		i {
+			color: #409eff;
+			font-size: 14px;
+		}
+	}
+}
+
+/* 图片预览 */
+.image-preview {
+	padding: 10px 0;
+	min-height: 40px;
+	display: flex;
+	align-items: flex-start;
+
+	.preview-image {
+		width: 200px;
+		height: auto;
+		border-radius: 8px;
+		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+		transition: transform 0.3s ease;
+
+		&:hover {
+			transform: scale(1.02);
+			cursor: pointer;
+		}
+	}
+}
+
+/* 状态包装 */
+.status-wrapper {
+	padding: 10px 12px;
+	min-height: 40px;
+	display: flex;
+	align-items: center;
+	background-color: #f8f9fa;
+	border: 1px solid #e9ecef;
+	border-radius: 6px;
+}
+
+/* 备注文本 */
+.remark-text {
+	color: #606266;
+	font-size: 14px;
+	line-height: 1.6;
+	padding: 12px 16px;
+	background-color: #f8f9fa;
+	border-left: 4px solid #409eff;
+	border-radius: 4px;
+	margin: 0;
+	min-height: 40px;
+	display: flex;
+	align-items: flex-start;
+}
+
+/* 弹窗底部 */
+.dialog-footer {
+	text-align: right;
+
+	.el-button {
+		padding: 10px 20px;
+		border-radius: 6px;
+		font-weight: 500;
+		transition: all 0.3s ease;
+
+		&.el-button--primary {
+			background: linear-gradient(135deg, #409eff 0%, #36a3f7 100%);
+			border: none;
+			box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
+
+			&:hover {
+				transform: translateY(-1px);
+				box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
+			}
+
+			&:active {
+				transform: translateY(0);
+			}
+		}
+
+		&:not(.el-button--primary) {
+			background-color: #fff;
+			border: 1.5px solid #dcdfe6;
+			color: #606266;
+
+			&:hover {
+				background-color: #f5f7fa;
+				border-color: #c0c4cc;
+			}
+		}
+	}
+}
+
+/* 响应式优化 */
+@media (max-width: 768px) {
+	.store-dialog {
+		::v-deep .el-dialog {
+			width: 95% !important;
+			margin: 5vh auto !important;
+		}
+	}
+
+	.dialog-content {
+		padding: 20px 16px;
+		
+		.el-row {
+			margin-left: -8px !important;
+			margin-right: -8px !important;
+			
+			.el-col {
+				padding-left: 8px !important;
+				padding-right: 8px !important;
+			}
+		}
+	}
+
+	.store-form {
+		::v-deep .el-form-item {
+			margin-bottom: 20px !important;
+			
+			.el-form-item__label {
+				width: 100px !important;
+				margin-bottom: 6px;
+			}
+		}
+	}
+
+	.bonus-rate-input {
+		.unit-text {
+			min-width: 28px;
+			padding: 0 6px;
+		}
+	}
+}
 </style>

+ 42 - 0
app/Http/Controllers/Admin/StoreController.php

@@ -92,6 +92,48 @@ class StoreController extends Backend
     }
 
     /**
+     * 新增商家
+     * @return array
+     */
+    public function add()
+    {
+        $params = request()->post();
+        if ($this->service->add($params)) {
+            return message($this->service->getError(), true);
+        } else {
+            return message($this->service->getError(), false);
+        }
+    }
+
+    /**
+     * 编辑商家
+     * @return array
+     */
+    public function edit()
+    {
+        $params = request()->post();
+        if ($this->service->edit($params)) {
+            return message($this->service->getError(), true);
+        } else {
+            return message($this->service->getError(), false);
+        }
+    }
+
+    /**
+     * 删除商家
+     * @return array
+     */
+    public function delete()
+    {
+        $params = request()->post();
+        if ($this->service->delete($params)) {
+            return message($this->service->getError(), true);
+        } else {
+            return message($this->service->getError(), false);
+        }
+    }
+
+    /**
      * 选项列表
      * @return mixed
      */

+ 3 - 2
app/Models/OrderModel.php

@@ -35,12 +35,13 @@ class OrderModel extends BaseModel
     }
 
     /**
-     * 订单商品
+     * 订单商品关联
+     * 通过 order_no 字段关联订单商品表
      * @return \Illuminate\Database\Eloquent\Relations\HasMany
      */
     public function orderGoods()
     {
-        return $this->hasMany(OrderGoodsModel::class, 'order_id', 'id')
+        return $this->hasMany(OrderGoodsModel::class, 'order_no', 'order_no')
             ->where('mark', 1);
     }
 

+ 20 - 0
app/Services/Common/GoodsService.php

@@ -197,6 +197,16 @@ class GoodsService extends BaseService
             return message('请选择商品分类', false);
         }
 
+        if (!isset($data['price']) || $data['price'] === '') {
+            return message('请填写商品单价', false);
+        }
+
+        if (!is_numeric($data['price'])) {
+            return message('商品单价格式不正确', false);
+        }
+
+        $data['price'] = (float)$data['price'];
+
         // 确保content字段存在(即使为空字符串或null)
         $contentValue = isset($data['content']) ? $data['content'] : '';
         if ($contentValue === null) {
@@ -290,6 +300,16 @@ class GoodsService extends BaseService
             return message('请选择商品分类', false);
         }
 
+        if (!isset($data['price']) || $data['price'] === '') {
+            return message('请填写商品单价', false);
+        }
+
+        if (!is_numeric($data['price'])) {
+            return message('商品单价格式不正确', false);
+        }
+
+        $data['price'] = (float)$data['price'];
+
         // 确保content字段存在(即使为空字符串或null)- 编辑时允许保存空字符串
         $contentValue = isset($data['content']) ? $data['content'] : '';
         if ($contentValue === null) {

+ 3 - 1
app/Services/Common/OrderService.php

@@ -92,11 +92,13 @@ class OrderService extends BaseService
             $query->where('refund_status', $params['refund_status']);
         }
 
-        // 关键词搜索(订单号、商品名称)
+        // 关键词搜索(订单号、商品名称、收货人手机
         if (isset($params['keyword']) && $params['keyword']) {
             $keyword = $params['keyword'];
             $query->where(function ($q) use ($keyword) {
                 $q->where('order_no', 'like', '%' . $keyword . '%')
+                    ->orWhere('receiver_name', 'like', '%' . $keyword . '%')
+                    ->orWhere('receiver_mobile', 'like', '%' . $keyword . '%')
                     ->orWhereHas('orderGoods', function ($q2) use ($keyword) {
                         $q2->where('goods_name', 'like', '%' . $keyword . '%');
                     });

+ 111 - 23
app/Services/Common/StoreService.php

@@ -78,7 +78,7 @@ class StoreService extends BaseService
                 if (isset($params['name']) && $params['name']) {
                     $query->where(function ($q) use ($params) {
                         $q->where('name', 'like', "%{$params['name']}%")
-                          ->orWhere('phone', 'like', "%{$params['name']}%");
+                            ->orWhere('phone', 'like', "%{$params['name']}%");
                     });
                 }
                 // 联系电话搜索
@@ -224,6 +224,9 @@ class StoreService extends BaseService
         if (empty($data['name'])) {
             return  message('请填写店铺名称', false);
         }
+        if (empty($data['address'])) {
+            $data['address'] = '';
+        }
 
         return parent::edit($data);
     }
@@ -253,30 +256,89 @@ class StoreService extends BaseService
 
         // 审核通过
         if ($status == 1) {
-            // 生成随机密码(8位数字字母组合)
-            $password = $this->generatePassword();
-
-            // 更新商家状态为已审核
-            $updateData = [
-                'status' => 1,
-                'confirm_remark' => $remark ?: '审核通过',
-                'update_time' => time()
-            ];
-
-            // 如果商家关联了用户,更新用户密码
-            if ($info->user_id > 0) {
-                $userModel = new \App\Models\UserModel();
-
-                $userModel->where('id', $info->user_id)->update([
-                    'password' => md5($password),
-                    'update_time' => time()
+            // 使用事务确保数据一致性
+            DB::beginTransaction();
+            try {
+                // 生成随机密码(8位数字字母组合)
+                $password = $this->generatePassword();
+                
+                // 生成管理账号用户名(使用商家手机号或店铺名称拼音)
+                $username = $this->generateUsername($info);
+                
+                // 检查用户名是否已存在
+                $existingUser = UserModel::where('username', $username)
+                    ->where('mark', 1)
+                    ->first();
+                
+                if ($existingUser) {
+                    // 如果用户名已存在,添加随机后缀
+                    $username = $username . mt_rand(100, 999);
+                }
+                
+                // 创建管理账户
+                $adminUserId = 0;
+                if ($info->user_id > 0) {
+                    // 如果商家已关联用户,更新用户信息
+                    $adminUserId = $info->user_id;
+                    UserModel::where('id', $info->user_id)->update([
+                        'username' => $username,
+                        'password' => get_password($password . $username),
+                        'realname' => $info->real_name,
+                        'mobile' => $info->phone,
+                        'status' => 1,
+                        'update_user' => $userId,
+                        'update_time' => time()
+                    ]);
+                } else {
+                    // 创建新的管理账户
+                    $userData = [
+                        'user_id' => $info->user_id, // 绑定商户用户ID(member表的ID)
+                        'username' => $username,
+                        'password' => get_password($password . $username),
+                        'realname' => $info->real_name,
+                        'mobile' => $info->phone,
+                        'status' => 1,
+                        'create_user' => $userId,
+                        'create_time' => time(),
+                        'update_user' => $userId,
+                        'update_time' => time(),
+                        'mark' => 1
+                    ];
+                    
+                    $adminUserId = UserModel::insertGetId($userData);
+                    
+                    // 更新商家表的user_id字段
+                    $info->user_id = $adminUserId;
+                }
+                
+                // 删除已存在的用户角色关系
+                \App\Models\UserRoleModel::where('user_id', $adminUserId)->delete();
+                
+                // 创建用户角色关系(role_id = 6)
+                \App\Models\UserRoleModel::insert([
+                    'user_id' => $adminUserId,
+                    'role_id' => 6
                 ]);
+                
+                // 更新商家状态为已审核
+                $updateData = [
+                    'status' => 1,
+                    'user_id' => $adminUserId,
+                    'confirm_remark' => $remark ?: '审核通过',
+                    'update_time' => time()
+                ];
+                
+                $this->model->where('id', $id)->update($updateData);
+                
+                DB::commit();
+                
+                $this->error = '审核通过,管理账号:' . $username . ',密码:' . $password;
+                return true;
+            } catch (\Exception $e) {
+                DB::rollBack();
+                $this->error = '审核失败:' . $e->getMessage();
+                return false;
             }
-
-            $this->model->where('id', $id)->update($updateData);
-
-            $this->error = '审核通过,商家密码:' . $password;
-            return true;
         }
         // 审核驳回
         elseif ($status == 3) {
@@ -315,6 +377,32 @@ class StoreService extends BaseService
     }
 
     /**
+     * 生成管理账号用户名
+     * @param object $storeInfo 商家信息
+     * @return string
+     */
+    private function generateUsername($storeInfo)
+    {
+        // 优先使用手机号作为用户名
+        if (!empty($storeInfo->phone)) {
+            return $storeInfo->phone;
+        }
+        
+        // 如果没有手机号,使用店铺名称 + 随机数
+        if (!empty($storeInfo->name)) {
+            // 移除特殊字符,只保留字母数字
+            $name = preg_replace('/[^a-zA-Z0-9]/', '', $storeInfo->name);
+            if (strlen($name) > 20) {
+                $name = substr($name, 0, 20);
+            }
+            return $name . mt_rand(1000, 9999);
+        }
+        
+        // 如果都没有,生成随机用户名
+        return 'store_' . mt_rand(100000, 999999);
+    }
+
+    /**
      * 获取当前登录商家信息
      * @return array
      */

+ 34 - 0
database/migrations/add_receiver_mobile_and_num_to_orders.sql

@@ -0,0 +1,34 @@
+-- 为订单表添加收货人手机号和商品数量字段
+-- 执行时间:2025-12-27
+
+-- 添加收货人手机号字段
+ALTER TABLE `lev_orders` 
+ADD COLUMN `receiver_mobile` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '收货人手机号' 
+AFTER `receiver_name`;
+
+-- 添加商品数量字段
+ALTER TABLE `lev_orders` 
+ADD COLUMN `num` int(10) NOT NULL DEFAULT '0' COMMENT '商品数量' 
+AFTER `store_id`;
+
+-- 添加索引以提高查询性能
+ALTER TABLE `lev_orders` 
+ADD INDEX `idx_receiver_mobile` (`receiver_mobile`);
+
+-- 更新现有数据(可选)
+-- 如果需要从订单商品表中统计数量,可以执行以下语句:
+-- UPDATE lev_orders o 
+-- SET num = (
+--     SELECT COALESCE(SUM(og.num), 0) 
+--     FROM lev_orders_goods og 
+--     WHERE og.order_no = o.order_no AND og.mark = 1
+-- ) 
+-- WHERE o.mark = 1;
+
+-- 验证更新结果
+SELECT 
+    COUNT(*) as total_orders,
+    COUNT(CASE WHEN receiver_mobile != '' THEN 1 END) as orders_with_mobile,
+    AVG(num) as avg_goods_count
+FROM lev_orders 
+WHERE mark = 1;

+ 1 - 0
routes/web.php

@@ -250,6 +250,7 @@ Route::post('/actionlog/delete', [ActionLogController::class, 'delete']);
 Route::get('/store/index', [\App\Http\Controllers\Admin\StoreController::class, 'index']);
 Route::get('/store/currentInfo', [\App\Http\Controllers\Admin\StoreController::class, 'currentInfo']); // 获取当前登录商家信息
 Route::get('/store/info', [\App\Http\Controllers\Admin\StoreController::class, 'info']);
+Route::post('/store/add', [\App\Http\Controllers\Admin\StoreController::class, 'add']);
 Route::post('/store/edit', [\App\Http\Controllers\Admin\StoreController::class, 'edit']);
 Route::post('/store/confirm', [\App\Http\Controllers\Admin\StoreController::class, 'confirm']);
 Route::post('/store/status', [\App\Http\Controllers\Admin\StoreController::class, 'status']);

+ 48 - 0
temp_check_syntax.php.bak

@@ -0,0 +1,48 @@
+<?php
+// 简单的语法检查脚本
+$file = 'app/Services/Common/StoreService.php';
+
+if (file_exists($file)) {
+    $content = file_get_contents($file);
+    
+    // 检查基本的PHP语法结构
+    $errors = [];
+    
+    // 检查类是否正确关闭
+    $openBraces = substr_count($content, '{');
+    $closeBraces = substr_count($content, '}');
+    
+    if ($openBraces !== $closeBraces) {
+        $errors[] = "大括号不匹配: 开括号 $openBraces 个,闭括号 $closeBraces 个";
+    }
+    
+    // 检查是否有重复的方法名
+    preg_match_all('/public function (\w+)\s*\(/', $content, $matches);
+    $methods = $matches[1];
+    $duplicates = array_diff_assoc($methods, array_unique($methods));
+    
+    if (!empty($duplicates)) {
+        $errors[] = "发现重复的方法: " . implode(', ', array_unique($duplicates));
+    }
+    
+    // 检查PHP标签
+    if (!preg_match('/^<\?php/', $content)) {
+        $errors[] = "文件必须以 <?php 开头";
+    }
+    
+    if (empty($errors)) {
+        echo "✅ StoreService.php 语法检查通过!\n";
+        echo "📊 统计信息:\n";
+        echo "   - 方法数量: " . count($methods) . "\n";
+        echo "   - 方法列表: " . implode(', ', $methods) . "\n";
+        echo "   - 大括号匹配: $openBraces 开 / $closeBraces 闭\n";
+    } else {
+        echo "❌ 发现以下问题:\n";
+        foreach ($errors as $error) {
+            echo "   - $error\n";
+        }
+    }
+} else {
+    echo "❌ 文件不存在: $file\n";
+}
+?>

+ 69 - 0
修改说明.md

@@ -0,0 +1,69 @@
+# 订单确认收货功能修改说明
+
+## 修改时间
+2025-12-28
+
+## 修改内容
+
+### 前端修改(order.vue)
+**文件路径**: `addons/admin/src/views/order/order.vue`
+
+#### 1. 按钮文字修改
+- **位置**: 表格操作列,已发货订单的操作按钮
+- **修改前**: `完成`
+- **修改后**: `确认收货`
+
+#### 2. 确认提示修改
+- **位置**: `handleComplete()` 和 `quickComplete()` 方法
+- **修改前**: `确定要将此订单标记为已完成吗?`
+- **修改后**: `确定要确认收货吗?`
+
+#### 3. 接口调用
+- **接口地址**: `/order/complete`
+- **请求方法**: POST
+- **请求参数**: `{ id: 订单ID }`
+
+#### 4. 错误处理
+前端已支持获取后端返回的 `msg` 或 `error` 字段作为错误提示
+
+### 后端接口(已存在,无需修改)
+**文件路径**: `app/Services/Common/OrderService.php`
+
+#### completeOrder() 方法
+- **功能**: 将已发货订单(status=3)标记为已完成(status=4)
+- **业务逻辑**:
+  1. 验证订单是否存在
+  2. 验证订单状态必须为"已发货"(status=3)
+  3. 计算商家佣金(订单金额的5%)
+  4. 更新订单状态为"已完成"(status=4)
+  5. 记录操作日志
+  6. 清除缓存
+
+#### 返回格式
+- **成功**: `['code' => 0, 'msg' => '订单已完成']`
+- **失败**: `['code' => 1, 'msg' => '错误信息']`
+
+可能的错误信息:
+- `参数错误` - 未传订单ID
+- `订单不存在` - 订单ID无效
+- `订单状态不正确,只有已发货订单可以完成` - 订单状态不是已发货
+- `操作失败` - 数据库更新失败
+
+## 测试建议
+
+1. **正常流程测试**
+   - 创建订单 → 支付 → 发货 → 确认收货
+   - 验证按钮文字显示为"确认收货"
+   - 验证确认提示文字正确
+   - 验证成功后订单状态变为"已完成"
+
+2. **异常流程测试**
+   - 对未发货订单点击确认收货,应提示"订单状态不正确"
+   - 对已完成订单不应显示确认收货按钮
+   - 网络异常时应显示友好的错误提示
+
+## 注意事项
+
+1. 前端文件位于 `addons/` 目录,该目录在 `.gitignore` 中,需要手动部署
+2. 修改后需要重新编译前端代码(如果使用了构建工具)
+3. 确认收货操作不可逆,请确保业务流程正确