diff --git a/api/index.js b/api/index.js
index e8bab20..fb4d81b 100644
--- a/api/index.js
+++ b/api/index.js
@@ -1,31 +1,162 @@
/**
- * API 接口汇总
- * 按模块分组管理接口
+ * API 请求封装
+ * 基于 uni.request,统一处理 Cookie 认证和错误
*/
-// 用户相关
+import { useConfigStore } from '../stores/config'
+// Storage Keys
+const STORAGE_KEY_COOKIE_SESSION = 'userCookieSession'
+
+// 错误码定义
+const ERR_COOKIE_EXPIRED = 'userCookieError'
+
+/**
+ * 获取 API 基础地址
+ */
+function getBaseUrl() {
+ const configStore = useConfigStore()
+ return configStore.getApiBaseUrl()
+}
+
+/**
+ * 获取保存的 Cookie 值
+ */
+function getCookie() {
+ try {
+ // 优先从 storage 读取(避免 Pinia 初始化时序问题)
+ const cookieStr = uni.getStorageSync(STORAGE_KEY_COOKIE_SESSION)
+ if (cookieStr) {
+ const cookie = JSON.parse(cookieStr)
+ return cookie?.Value ?? ''
+ }
+ } catch {
+ // ignore
+ }
+ return ''
+}
+
+/**
+ * 请求封装 - 统一错误处理
+ */
+function request(options) {
+ return new Promise((resolve, reject) => {
+ const baseUrl = getBaseUrl()
+ if (!baseUrl) {
+ reject({ errCode: -1, message: 'API地址未配置' })
+ return
+ }
+
+ // GET 请求不注入 cookie,POST 请求注入 cookie
+ let data = options.data || {}
+ if (options.method === 'POST') {
+ const cookie = getCookie()
+ // 后端期望格式: { data: {...业务数据}, userCookieValue: "xxx" }
+ data = {
+ data: data, // 业务数据包装在 data 字段
+ userCookieValue: cookie || undefined
+ }
+ }
+
+ uni.request({
+ url: baseUrl + options.path,
+ method: options.method || 'GET',
+ data,
+ header: {
+ 'Content-Type': 'application/json'
+ },
+ timeout: options.timeout || 10000,
+ success: (res) => {
+ const data = res.data
+
+ // 检查 Cookie 过期
+ if (data?.err_code === ERR_COOKIE_EXPIRED || data?.err_code === -44) {
+ // 清除存储的 Cookie
+ uni.removeStorageSync(STORAGE_KEY_COOKIE_SESSION)
+ uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' })
+ reject({ errCode: -44, message: '登录已过期' })
+ return
+ }
+
+ if (res.statusCode === 200) {
+ resolve({
+ errCode: data?.err_code === 0 ? 0 : -1,
+ data: data?.return || null,
+ raw: data
+ })
+ } else {
+ uni.showToast({ title: '服务异常', icon: 'none' })
+ reject({ errCode: -1, message: '服务异常' })
+ }
+ },
+ fail: (err) => {
+ uni.showToast({ title: '网络错误', icon: 'none' })
+ reject({ errCode: -2, message: '网络错误', detail: err })
+ }
+ })
+ })
+}
+
+/**
+ * API 接口汇总
+ */
export const api = {
/**
- * GET 请求(一般不需要认证)
+ * GET 请求(不需要认证)
*/
- get(path) {
-
- },
+ get(path, data = {}) {
+ return request({
+ path,
+ method: 'GET',
+ data
+ })
+ },
/**
- * POST JSON
+ * POST JSON 请求(需要认证)
*/
- post(path, data = {},callback) {
- console.log("post")
- },
+ post(path, data = {}) {
+ return request({
+ path,
+ method: 'POST',
+ data // 业务数据,会被包装成 { data, userCookieValue }
+ })
+ },
/**
* POST FormData(文件上传)
*/
- upload(path, file) {
-
- },
+ upload(path, fileData) {
+ return new Promise((resolve, reject) => {
+ const baseUrl = getBaseUrl()
+ const cookie = getCookie()
+
+ uni.uploadFile({
+ url: baseUrl + path,
+ filePath: fileData.uri,
+ name: fileData.name || 'file',
+ formData: {
+ cookie: cookie || ''
+ },
+ success: (res) => {
+ try {
+ const data = JSON.parse(res.data)
+ resolve({
+ errCode: data?.err_code === 0 ? 0 : -1,
+ data: data?.return || null,
+ raw: data
+ })
+ } catch {
+ reject({ errCode: -1, message: '解析响应失败' })
+ }
+ },
+ fail: (err) => {
+ uni.showToast({ title: '上传失败', icon: 'none' })
+ reject({ errCode: -2, message: '上传失败', detail: err })
+ }
+ })
+ })
+ }
}
-export default api
\ No newline at end of file
+export default api
diff --git a/api/purchase.js b/api/purchase.js
new file mode 100644
index 0000000..2e2ab21
--- /dev/null
+++ b/api/purchase.js
@@ -0,0 +1,33 @@
+/**
+ * 采购订单 API
+ */
+import api from './index.js'
+
+export const purchaseApi = {
+ // 获取订单列表
+ getOrders(data = {}) {
+ return api.post('/purchase/getorders', data)
+ },
+
+ // 获取订单详情
+ getOrder(data = {}) {
+ return api.post('/purchase/getorder', data)
+ },
+
+ // 获取订单数量统计
+ getOrderCount() {
+ return api.post('/purchase/getordercount', {})
+ },
+
+ // 更新订单状态
+ updateOrderStatus(data = {}) {
+ return api.post('/purchase/updatestatus', data)
+ },
+
+ // 新增订单
+ addOrder(data = {}) {
+ return api.post('/purchase/addorder', data)
+ }
+}
+
+export default purchaseApi
diff --git a/api/schedule.js b/api/schedule.js
new file mode 100644
index 0000000..b49ecb6
--- /dev/null
+++ b/api/schedule.js
@@ -0,0 +1,28 @@
+/**
+ * 日程管理 API
+ */
+import api from './index.js'
+
+export const scheduleApi = {
+ // 获取日程列表
+ getEvents(data = {}) {
+ return api.post('/schedule/getevents', data)
+ },
+
+ // 新增日程
+ addEvent(data = {}) {
+ return api.post('/schedule/addevent', data)
+ },
+
+ // 编辑日程
+ editEvent(data = {}) {
+ return api.post('/schedule/editevent', data)
+ },
+
+ // 删除日程
+ deleteEvent(data = {}) {
+ return api.post('/schedule/deleevent', data)
+ }
+}
+
+export default scheduleApi
diff --git a/api/user.js b/api/user.js
index 532c7f6..a9656ba 100644
--- a/api/user.js
+++ b/api/user.js
@@ -1,9 +1,86 @@
-import api from ".";
+/**
+ * 用户相关 API
+ */
+import api from './index'
export const userApi = {
-
- login(username, password, remember) {
- return api.post('/users/login', { username, password, remember })
- },
-
-}
\ No newline at end of file
+ /**
+ * 用户登录
+ * @param {string} username - 用户名
+ * @param {string} password - 密码
+ * @param {boolean} remember - 是否记住登录
+ * @returns {Promise<{errCode, data: {cookie}}>}
+ */
+ login(username, password, remember = true) {
+ return api.post('/users/login', {
+ username,
+ password,
+ remember
+ })
+ },
+
+ /**
+ * 用户注册
+ * @param {string} username - 用户名
+ * @param {string} email - 邮箱
+ * @param {string} password - 密码
+ */
+ register(username, email, password) {
+ return api.post('/users/register', {
+ username,
+ useremail: email,
+ userpass: password
+ })
+ },
+
+ /**
+ * 获取当前用户信息
+ * @returns {Promise<{errCode, data: {user, userInfo}}>}
+ */
+ getUserInfo() {
+ return api.post('/users/getinfo', {})
+ },
+
+ /**
+ * 修改密码
+ * @param {string} oldPass - 旧密码
+ * @param {string} newPass - 新密码
+ */
+ changePassword(oldPass, newPass) {
+ return api.post('/users/changePassword', {
+ oldpass: oldPass,
+ newpass: newPass
+ })
+ },
+
+ /**
+ * 修改邮箱
+ * @param {string} newEmail - 新邮箱
+ */
+ changeEmail(newEmail) {
+ return api.post('/users/changeEmail', {
+ newemail: newEmail
+ })
+ },
+
+ /**
+ * 更新用户信息
+ * @param {Object} data - 用户信息 { username, remark, birthday }
+ */
+ updateInfo(data) {
+ return api.post('/users/updateInfo', data)
+ },
+
+ /**
+ * 上传头像
+ * @param {string} fileUri - 文件本地路径
+ */
+ updateAvatar(fileUri) {
+ return api.upload('/users/updateAvatar', {
+ name: 'file',
+ uri: fileUri
+ })
+ }
+}
+
+export default userApi
diff --git a/api/users.js b/api/users.js
new file mode 100644
index 0000000..19dcd18
--- /dev/null
+++ b/api/users.js
@@ -0,0 +1,13 @@
+/**
+ * 用户信息 API
+ */
+import api from './index.js'
+
+export const usersApi = {
+ // 通过用户ID获取用户信息(GET 请求)
+ getUserInfoFromUserID(userID) {
+ return api.get('/users/getuserinfo/' + userID)
+ }
+}
+
+export default usersApi
diff --git a/api/warehouse.js b/api/warehouse.js
new file mode 100644
index 0000000..b2a3926
--- /dev/null
+++ b/api/warehouse.js
@@ -0,0 +1,66 @@
+import api from './index.js'
+
+export const warehouseApi = {
+ // 获取容器列表
+ listContainer(params = {}) {
+ return api.post('/warehouse/list_container', params)
+ },
+
+ // 获取容器详情
+ getContainer(id) {
+ return api.post('/warehouse/get_container', { id })
+ },
+
+ // 新增容器
+ addContainer(data) {
+ return api.post('/warehouse/add_container', data)
+ },
+
+ // 更新容器
+ updateContainer(data) {
+ return api.post('/warehouse/update_container', data)
+ },
+
+ // 删除容器
+ deleteContainer(id) {
+ return api.post('/warehouse/delete_container', { id })
+ },
+
+ // 获取物品列表
+ listItem(params = {}) {
+ return api.post('/warehouse/list_item', params)
+ },
+
+ // 获取物品详情
+ getItem(id) {
+ return api.post('/warehouse/get_item', { id })
+ },
+
+ // 新增物品
+ addItem(data) {
+ return api.post('/warehouse/add_item', data)
+ },
+
+ // 更新物品
+ updateItem(data) {
+ return api.post('/warehouse/update_item', data)
+ },
+
+ // 删除物品
+ deleteItem(id) {
+ return api.post('/warehouse/delete_item', { id })
+ },
+
+ // 移动物品
+ moveItem(id, containerId) {
+ return api.post('/warehouse/move_item', {
+ item_id: id,
+ new_container: containerId
+ })
+ },
+
+ // 获取仓库统计
+ getStats() {
+ return api.post('/warehouse/get_stats', {})
+ }
+}
diff --git a/api/work_order.js b/api/work_order.js
new file mode 100644
index 0000000..f3b1664
--- /dev/null
+++ b/api/work_order.js
@@ -0,0 +1,43 @@
+import api from './index.js'
+
+export const workOrderApi = {
+ // 获取工单列表
+ list(params = {}) {
+ return api.post('/work_order/list', params)
+ },
+
+ // 获取工单详情
+ get(id) {
+ return api.post('/work_order/get', { id })
+ },
+
+ // 获取工单统计
+ getCount() {
+ return api.post('/work_order/count', {})
+ },
+
+ // 新增工单
+ add(data) {
+ return api.post('/work_order/add', data)
+ },
+
+ // 更新工单
+ update(data) {
+ return api.post('/work_order/update', data)
+ },
+
+ // 删除工单
+ delete(id) {
+ return api.post('/work_order/delete', { id })
+ },
+
+ // 提交进度
+ commit(data) {
+ return api.post('/work_order/commit', data)
+ },
+
+ // 搜索采购订单
+ searchPurchaseOrders(query, limit = 10) {
+ return api.post('/work_order/search_purchase_orders', { search: query, limit })
+ }
+}
diff --git a/manifest.json b/manifest.json
index f5a5576..5e4e73f 100644
--- a/manifest.json
+++ b/manifest.json
@@ -68,5 +68,10 @@
"uniStatistics" : {
"enable" : false
},
- "vueVersion" : "3"
+ "vueVersion" : "3",
+ "h5" : {
+ "devServer" : {
+ "port" : 5174
+ }
+ }
}
diff --git a/pages.json b/pages.json
index 988f9b9..c1c62eb 100644
--- a/pages.json
+++ b/pages.json
@@ -13,9 +13,63 @@
}
},
{
- "path": "pages/message/message",
+ "path": "pages/order/order-detail",
"style": {
- "navigationBarTitleText": "消息"
+ "navigationBarTitleText": "订单详情"
+ }
+ },
+ {
+ "path": "pages/order/order-add",
+ "style": {
+ "navigationBarTitleText": "新增订单"
+ }
+ },
+ {
+ "path": "pages/workorder/workorder",
+ "style": {
+ "navigationBarTitleText": "工单"
+ }
+ },
+ {
+ "path": "pages/workorder/add-workorder",
+ "style": {
+ "navigationBarTitleText": "新建工单"
+ }
+ },
+ {
+ "path": "pages/workorder/show-workorder",
+ "style": {
+ "navigationBarTitleText": "工单详情"
+ }
+ },
+ {
+ "path": "pages/workorder/edit-workorder",
+ "style": {
+ "navigationBarTitleText": "编辑工单"
+ }
+ },
+ {
+ "path": "pages/warehouse/warehouse",
+ "style": {
+ "navigationBarTitleText": "仓库"
+ }
+ },
+ {
+ "path": "pages/warehouse/item-detail",
+ "style": {
+ "navigationBarTitleText": "物品详情"
+ }
+ },
+ {
+ "path": "pages/warehouse/add-item",
+ "style": {
+ "navigationBarTitleText": "新增物品"
+ }
+ },
+ {
+ "path": "pages/warehouse/item-edit",
+ "style": {
+ "navigationBarTitleText": "编辑物品"
}
},
{
@@ -35,6 +89,12 @@
"style": {
"navigationBarTitleText": "设置"
}
+ },
+ {
+ "path": "pages/message/message",
+ "style": {
+ "navigationBarTitleText": "消息"
+ }
}
],
"globalStyle": {
@@ -65,10 +125,16 @@
"text": "订单"
},
{
- "pagePath": "pages/message/message",
- "iconPath": "static/tabbar/message.png",
- "selectedIconPath": "static/tabbar/message-active.png",
- "text": "消息"
+ "pagePath": "pages/workorder/workorder",
+ "iconPath": "static/tabbar/workorder.png",
+ "selectedIconPath": "static/tabbar/workorder-active.png",
+ "text": "工单"
+ },
+ {
+ "pagePath": "pages/warehouse/warehouse",
+ "iconPath": "static/tabbar/warehouse.png",
+ "selectedIconPath": "static/tabbar/warehouse-active.png",
+ "text": "仓库"
},
{
"pagePath": "pages/user/user",
diff --git a/pages/index/index.vue b/pages/index/index.vue
index 9d520a7..82ca69b 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -1,23 +1,414 @@
- 主页
+
+
+ {{ welcomeText }}
+ {{ todayDisplay }}
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+ {{ formatScheduleDate(schedule) }}
+
+
+ {{ schedule.Title }}
+ 创建人: {{ getCreatorName(schedule.UserID) }}
+
+
+
+
+
+
+ 今日暂无日程
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+ {{ pendingCount || '—' }}
+ 待处理
+
+
+ {{ arrivedCount || '—' }}
+ 已到达
+
+
+ {{ receivedCount || '—' }}
+ 已收件
+
+
+
+
+
+
+
+
+
+ 📦
+ 订单管理
+
+
+ 🏭
+ 仓库管理
+
+
+
diff --git a/pages/login/login.vue b/pages/login/login.vue
index b5b9331..badd15f 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -1,67 +1,148 @@
+
+
+
+ OPS 管理系统
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ errorMsg }}
@@ -69,14 +150,41 @@ const handleLogin = () => {
.container {
flex: 1;
display: flex;
- justify-content: center;
+ flex-direction: column;
align-items: center;
- background-color: #f5f5f5;
- padding: 40rpx;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ min-height: 100vh;
+ padding: 100rpx 40rpx 40rpx;
}
+/* Logo 区域 */
+.logo-section {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin-bottom: 80rpx;
+}
+
+.logo-img {
+ width: 160rpx;
+ height: 160rpx;
+ margin-bottom: 20rpx;
+}
+
+.app-name {
+ font-size: 40rpx;
+ font-weight: bold;
+ color: #FFFFFF;
+ letter-spacing: 4rpx;
+}
+
+/* 表单区域 */
.form {
width: 100%;
+ background-color: #FFFFFF;
+ border-radius: 20rpx;
+ padding: 40rpx;
+ box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.1);
}
.input {
@@ -84,19 +192,56 @@ const handleLogin = () => {
line-height: 90rpx;
padding: 0 30rpx;
margin-bottom: 30rpx;
- background-color: #FFFFFF;
+ background-color: #F8F8F8;
border-radius: 10rpx;
font-size: 28rpx;
}
+.input:last-of-type {
+ margin-bottom: 20rpx;
+}
+
+/* 记住登录 */
+.remember-row {
+ margin-bottom: 30rpx;
+}
+
+.remember-label {
+ display: flex;
+ align-items: center;
+ font-size: 26rpx;
+ color: #666;
+}
+
+.remember-label checkbox {
+ margin-right: 10rpx;
+}
+
+/* 登录按钮 */
.submit-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
- background-color: #007AFF;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #FFFFFF;
font-size: 32rpx;
+ border-radius: 45rpx;
+ border: none;
+}
+
+.submit-btn[disabled] {
+ background: #CCCCCC;
+}
+
+/* 错误提示 */
+.error-tip {
+ width: 100%;
+ margin-top: 30rpx;
+ padding: 20rpx;
+ background-color: rgba(255, 255, 255, 0.9);
border-radius: 10rpx;
- margin-top: 20rpx;
+ text-align: center;
+ color: #FF3B30;
+ font-size: 26rpx;
}
diff --git a/pages/order/order-add.vue b/pages/order/order-add.vue
new file mode 100644
index 0000000..2ad2362
--- /dev/null
+++ b/pages/order/order-add.vue
@@ -0,0 +1,679 @@
+
+
+
+
+
+
+
+
+ 基本信息
+
+
+
+ 配件名称
+
+
+
+
+
+ 备注
+
+ {{ form.remark.length }}/256
+
+
+
+
+ 采购链接
+
+
+
+
+
+ 款式标签
+
+
+
+ {{ tag }}
+ ×
+
+
+
+
+
+
+
+
+
+ 费用明细
+
+
+
+
+
+ {{ costType[item.type] || item.type }}
+ {{ item.int }}
+ {{ item.cost }}
+ {{ item.costt }}
+ {{ currencyOptions[item.currencytype] || item.currencytype }}
+ 删除
+
+
+
+
+
+
+
+ 费用类型
+
+
+ {{ costTypeOptions[costTypeIndex].label }}
+
+
+
+
+ 数量
+
+
+
+
+
+ 单价
+
+
+
+ 货币
+
+
+ {{ currencyOptionsList[currencyIndex].label }}
+
+
+
+
+
+ 总计:{{ currencyOptionsList[currencyIndex].symbol }}{{ newCostTotal }}
+
+ 单价必须大于0
+
+
+
+
+
+
+ 图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/order-detail.vue b/pages/order/order-detail.vue
new file mode 100644
index 0000000..577ce9c
--- /dev/null
+++ b/pages/order/order-detail.vue
@@ -0,0 +1,436 @@
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+ 标题
+ {{ order.Title || '-' }}
+
+
+ 链接
+ {{ order.Link }}
+
+
+ 备注
+ {{ order.Remark }}
+
+
+ 创建时间
+ {{ formatDate(order.CreatedAt) }}
+
+
+
+
+
+
+
+
+ {{ opt.label }}
+
+
+
+
+
+
+
+
+
+
+ {{ getCostTypeText(cost.CostType) }}
+ {{ cost.Quantity }}
+ {{ formatPrice(cost.Price) }}
+ {{ formatPrice(cost.Price * cost.Quantity) }}
+
+
+
+ {{ g.currency }} {{ g.total }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #{{ wo.id }}
+ {{ wo.title }}
+ {{ getWOStatusText(wo.status) }}
+
+
+
+
+
+
+
+
+
+
+
+ by {{ getUsernameById(commit.userId) }}
+
+
+
+
+
+
+
+
+
+ 未找到订单信息
+
+
+
+
+
+
+
+
+ 新状态
+ {{ getStatusText(pendingStatus) }}
+
+
+ 备注
+
+
+
+ 图片
+
+
+
+ ×
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/order.vue b/pages/order/order.vue
index 6a5d48a..2cac4f9 100644
--- a/pages/order/order.vue
+++ b/pages/order/order.vue
@@ -1,23 +1,433 @@
-
- 订单
-
+
+
+
+
+
+ {{ stats.pending || 0 }}
+ 待处理
+
+
+ {{ stats.ordered || 0 }}
+ 已下单
+
+
+ {{ stats.arrived || 0 }}
+ 已到达
+
+
+ {{ stats.received || 0 }}
+ 已收件
+
+
+
+
+
+
+ 搜索
+
+
+
+ {{ currentFilter.label || '全部状态' }}
+
+
+
+
+
+
+ 加载中...
+
+
+ 暂无订单
+
+
+
+
+
+ {{ item.Title || '无标题' }}
+
+
+
+
+
+
+ 加载更多...
+
+
+ 加载更多
+
+
+
+
diff --git a/pages/user/user.vue b/pages/user/user.vue
index f946741..6317fe5 100644
--- a/pages/user/user.vue
+++ b/pages/user/user.vue
@@ -1,25 +1,135 @@
+
-
+
+
+
+
+ 👤
+
+ 您还未登录
+
+
+
+
+
+
+
+
+
+
+ 👤
+
+
+
+ {{ userStore.username || '未设置昵称' }}
+ {{ getUserRole() }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/warehouse/add-item.vue b/pages/warehouse/add-item.vue
new file mode 100644
index 0000000..ab3aa74
--- /dev/null
+++ b/pages/warehouse/add-item.vue
@@ -0,0 +1,314 @@
+
+
+
+
+
+
+
+ 名称 *
+
+
+
+
+ 编号
+
+
+
+
+ 备注
+
+
+
+
+
+
+ 物品图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+ 保存
+
+
+
+
+
+
+
+
+
diff --git a/pages/warehouse/item-detail.vue b/pages/warehouse/item-detail.vue
new file mode 100644
index 0000000..7c27726
--- /dev/null
+++ b/pages/warehouse/item-detail.vue
@@ -0,0 +1,670 @@
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+ 名称
+ {{ item.Name }}
+
+
+ 编号
+ {{ item.SerialNumber }}
+
+
+ 规格
+ {{ item.Specification }}
+
+
+ 位置
+ {{ item.ContainerBreadcrumb }}
+
+
+ 备注
+ {{ item.Remark }}
+
+
+ 创建时间
+ {{ formatDate(item.CreatedAt) }}
+
+
+ 更新时间
+ {{ formatDate(item.UpdatedAt) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ commit.OldContainerBreadcrumb || '无' }} → {{ commit.NewContainerBreadcrumb || '已移除' }}
+
+ {{ formatDate(commit.CreatedAt) }}
+
+
+
+
+
+
+
+
+
+ #{{ wo.id }}
+ {{ wo.title }}
+ {{ getWorkOrderStatusText(wo.status) }}
+
+
+
+
+
+
+ 未找到物品信息
+
+
+
+
+
+
+ + 工单
+
+
+ 编辑
+
+
+ 移动
+
+
+
+
+
+
+
+
+
+ 选择容器
+
+
+ {{ selectedContainer.Title || '请选择目标容器' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/warehouse/item-edit.vue b/pages/warehouse/item-edit.vue
new file mode 100644
index 0000000..a93c185
--- /dev/null
+++ b/pages/warehouse/item-edit.vue
@@ -0,0 +1,351 @@
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+ 名称 *
+
+
+
+
+ 编号
+
+
+
+
+ 备注
+
+
+
+
+
+
+ 物品图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+ 保存
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/warehouse/warehouse.vue b/pages/warehouse/warehouse.vue
new file mode 100644
index 0000000..6dd73da
--- /dev/null
+++ b/pages/warehouse/warehouse.vue
@@ -0,0 +1,1006 @@
+
+
+
+
+
+
+
+ 全部
+
+
+ 仅物品
+
+
+ 🔍
+
+
+
+
+
+
+
+ ‹ 返回上级
+
+
+ {{ crumb.title }}
+ /
+
+
+ ✎ 编辑
+
+
+
+ 加载中...
+
+
+ 暂无内容
+
+
+
+
+
+ 📁
+
+
+ {{ item.Title }}
+
+ 子容器: {{ item.ChildCount }} | 物品: {{ item.ItemCount }}
+
+
+ ›
+
+
+
+
+
+ 📦
+
+
+ {{ item.Name }}
+ 编号: {{ item.SerialNumber }}
+
+ ›
+
+
+
+
+
+
+
+ 加载中...
+
+
+ 暂无物品
+
+
+
+
+
+ 📦
+
+
+ {{ item.Name }}
+ 编号: {{ item.SerialNumber }}
+
+ 位置: {{ item.ContainerBreadcrumb }}
+
+
+ ›
+
+
+
+ 加载更多
+
+
+
+
+
+
+
+
+
+ +
+ 新增容器
+
+
+ +
+ 新增物品
+
+
+
+
+ +
+ 新增容器
+
+
+
+ +
+ 新增物品
+
+
+
+
+
+
+
+
+
+ 搜索
+
+
+
+
+
+
+
+
+
+
+
+ 名称 *
+
+
+
+
+ 所属目录
+
+ {{ selectedParentName }}
+ ›
+
+
+
+
+ 备注
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📁
+ {{ container.Title }}
+ ✓
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/workorder/add-workorder.vue b/pages/workorder/add-workorder.vue
new file mode 100644
index 0000000..a2155f3
--- /dev/null
+++ b/pages/workorder/add-workorder.vue
@@ -0,0 +1,566 @@
+
+
+
+
+
+
+
+
+
+ 工单标题 *
+
+
+
+
+
+ 问题描述
+
+
+
+
+
+ 关联物品
+
+
+
+
+ {{ selectedItem.Name }}
+ {{ selectedItem.SerialNumber }}
+
+ 清除
+
+
+
+
+
+
+
+
+
+ {{ item.Name }}
+ {{ item.SerialNumber }}
+
+
+
+
+ 搜索中...
+
+
+
+ 未找到匹配的物品
+
+
+
+
+
+
+ 图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+
+ 提交中...
+ 提交工单
+
+
+
+
+
+
+
+
+
diff --git a/pages/workorder/edit-workorder.vue b/pages/workorder/edit-workorder.vue
new file mode 100644
index 0000000..973d984
--- /dev/null
+++ b/pages/workorder/edit-workorder.vue
@@ -0,0 +1,491 @@
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+ {{ pageError }}
+
+
+
+
+
+
+ 工单标题 *
+
+
+
+
+
+ 问题描述
+
+
+
+
+
+ 图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+ 保存中...
+ 保存修改
+
+
+
+
+
+
+
+
+ 确认删除
+ 确定要删除这个工单吗?此操作无法撤销。
+
+ 取消
+ 删除
+
+
+
+
+
+
+
+
+
diff --git a/pages/workorder/show-workorder.vue b/pages/workorder/show-workorder.vue
new file mode 100644
index 0000000..4f72290
--- /dev/null
+++ b/pages/workorder/show-workorder.vue
@@ -0,0 +1,1181 @@
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+ 工单不存在
+
+
+
+
+
+
+
+
+
+ 标题
+ {{ order.Title || '-' }}
+
+
+
+ 描述
+ {{ order.Description }}
+
+
+
+
+ 关联物品
+
+
+ {{ item.Name }}
+ -{{ item.SerialNumber }}
+
+
+
+
+
+
+ 关联采购订单
+
+
+ #{{ po.id }}
+ {{ po.title || '' }}
+ {{ getPurchaseStatusText(po.status) }}
+
+
+
+
+
+
+
+ 图片
+
+
+
+
+
+
+
+
+
+ 进度历史
+
+
+ 暂无进度记录
+
+
+
+
+
+
+
+
+ 创建工单
+
+
+
+
+
+
+
+
+
+
+ 关联采购订单:
+
+ #{{ po.id }}
+ {{ po.title || '' }}
+ {{ getPurchaseStatusText(po.status) }}
+
+
+
+
+
+
+
+
+
+ 新增进度
+
+
+ 进度状态
+
+
+ {{ selectedStatus.label || '请选择状态' }}
+
+
+
+
+
+
+ 关联采购订单
+
+
+
+
+ #{{ po.id }} {{ po.title || '' }}
+ ×
+
+
+
+
+
+
+
+
+
+
+
+ #{{ po.id }}
+ {{ po.title || '-' }}
+ {{ getPurchaseStatusText(po.status) }}
+
+
+
+ 未找到匹配的订单
+
+
+
+
+ 备注
+
+
+
+
+ 图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+ 提交中...
+ 提交进度
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/workorder/workorder.vue b/pages/workorder/workorder.vue
new file mode 100644
index 0000000..6170270
--- /dev/null
+++ b/pages/workorder/workorder.vue
@@ -0,0 +1,382 @@
+
+
+
+
+
+
+ {{ stats.pending || 0 }}
+ 待处理
+
+
+ {{ stats.checked || 0 }}
+ 已检查
+
+
+ {{ stats.parts_ordered || 0 }}
+ 已下单零件
+
+
+ {{ stats.repaired || 0 }}
+ 已维修
+
+
+
+
+
+
+ {{ currentFilter.label || '全部状态' }}
+
+
+
+
+
+
+ 加载中...
+
+
+ 暂无工单
+
+
+
+
+
+ {{ item.Title || '无标题' }}
+ {{ item.Description || '无描述' }}
+
+
+
+
+
+ 加载更多...
+
+
+ 加载更多
+
+
+
+
+
+
+
+
+
diff --git a/static/logo.png b/static/logo.png
index b5771e2..3a22158 100644
Binary files a/static/logo.png and b/static/logo.png differ
diff --git a/static/tabbar/warehouse-active.png b/static/tabbar/warehouse-active.png
new file mode 100644
index 0000000..13a5e7b
Binary files /dev/null and b/static/tabbar/warehouse-active.png differ
diff --git a/static/tabbar/warehouse.png b/static/tabbar/warehouse.png
new file mode 100644
index 0000000..d2b6e8a
Binary files /dev/null and b/static/tabbar/warehouse.png differ
diff --git a/static/tabbar/workorder-active.png b/static/tabbar/workorder-active.png
new file mode 100644
index 0000000..13a5e7b
Binary files /dev/null and b/static/tabbar/workorder-active.png differ
diff --git a/static/tabbar/workorder.png b/static/tabbar/workorder.png
new file mode 100644
index 0000000..d2b6e8a
Binary files /dev/null and b/static/tabbar/workorder.png differ
diff --git a/stores/config.js b/stores/config.js
index 3d4a141..0569a1e 100644
--- a/stores/config.js
+++ b/stores/config.js
@@ -25,6 +25,15 @@ export const useConfigStore = defineStore('config', () => {
}
return apiBaseUrl.value
}
+
+ // 获取图片基础 URL(去掉末尾的 /api 部分)
+ const getFileBaseUrl = () => {
+ const base = getApiBaseUrl()
+ if (base) {
+ return base.replace(/\/api$/, '')
+ }
+ return base
+ }
// 设置主题
@@ -39,6 +48,7 @@ export const useConfigStore = defineStore('config', () => {
theme,
setApiBaseUrl,
getApiBaseUrl,
+ getFileBaseUrl,
setTheme
}
})
diff --git a/stores/user.js b/stores/user.js
index 1334e01..8ea8211 100644
--- a/stores/user.js
+++ b/stores/user.js
@@ -1,18 +1,151 @@
import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import { useConfigStore } from './config'
-export const useUserStore = defineStore('user', {
- state: () => ({
- username: '',
- token: ''
- }),
- actions: {
- setUser(username, token) {
- this.username = username
- this.token = token
- },
- logout() {
- this.username = ''
- this.token = ''
+// Storage Keys
+const STORAGE_KEY_COOKIE = 'userCookie'
+const STORAGE_KEY_COOKIE_SESSION = 'userCookieSession'
+
+/**
+ * 加载 JSON(永久存储)
+ */
+function loadJson(key) {
+ try {
+ const raw = uni.getStorageSync(key)
+ return raw ? JSON.parse(raw) : null
+ } catch {
+ return null
+ }
+}
+
+/**
+ * 清除存储
+ */
+function removeStorage() {
+ uni.removeStorageSync(STORAGE_KEY_COOKIE)
+ uni.removeStorageSync(STORAGE_KEY_COOKIE_SESSION)
+}
+
+export const useUserStore = defineStore('user', () => {
+ // ── State ──
+ const userCookie = ref(null) // Cookie 对象 { Value, Name, ExpiresAt, Remember }
+ const user = ref(null) // 用户基本信息 { ID, Name, ... }
+ const userInfo = ref(null) // 用户详细信息 { Username, FirstName, ... }
+
+ // ── Getters ──
+ /** Cookie 值字符串 */
+ const cookieValue = computed(() => userCookie.value?.Value ?? '')
+
+ /** 是否已登录 */
+ const isLoggedIn = computed(() => !!userCookie.value)
+
+ /** 用户名 */
+ const username = computed(() => {
+ return userInfo.value?.Username || user.value?.Name || ''
+ })
+
+ /** 头像 URL */
+ const avatarUrl = computed(() => {
+ if (userInfo.value?.AvatarPath) {
+ const configStore = useConfigStore()
+ return configStore.getApiBaseUrl() + '/static/avatar/' + userInfo.value.AvatarPath
+ }
+ return null
+ })
+
+ // ── Actions ──
+
+ /**
+ * 登录 - 保存 Cookie 并获取用户信息
+ * @param {Object} cookie - 后端返回的 cookie 对象
+ */
+ function login(cookie) {
+ userCookie.value = cookie
+
+ // 持久化存储
+ if (cookie.Remember) {
+ uni.setStorageSync(STORAGE_KEY_COOKIE, JSON.stringify(cookie))
+ }
+ // 会话存储(始终保存)
+ uni.setStorageSync(STORAGE_KEY_COOKIE_SESSION, JSON.stringify(cookie))
+
+ // 检查 cookie 是否过期
+ if (cookie.ExpiresAt && new Date(cookie.ExpiresAt) < new Date()) {
+ logout()
+ return
+ }
+
+ // 获取用户信息
+ fetchUserInfo()
+ }
+
+ /**
+ * 退出登录
+ */
+ function logout() {
+ userCookie.value = null
+ user.value = null
+ userInfo.value = null
+ removeStorage()
+ }
+
+ /**
+ * 获取用户信息
+ */
+ async function fetchUserInfo() {
+ try {
+ const { api } = await import('../api/index.js')
+ const res = await api.post('/users/getinfo', {})
+ if (res.errCode === 0 && res.data) {
+ user.value = res.data.user ?? null
+ userInfo.value = res.data.userInfo ?? null
+ }
+ } catch (e) {
+ console.error('获取用户信息失败', e)
}
}
+
+ /**
+ * 应用启动时恢复登录状态
+ */
+ function restoreSession() {
+ // 优先使用会话存储,否则用永久存储
+ let cookieStr = uni.getStorageSync(STORAGE_KEY_COOKIE_SESSION)
+ if (!cookieStr) {
+ cookieStr = uni.getStorageSync(STORAGE_KEY_COOKIE)
+ }
+
+ if (cookieStr) {
+ try {
+ const cookie = JSON.parse(cookieStr)
+ // 检查是否过期
+ if (cookie.ExpiresAt && new Date(cookie.ExpiresAt) < new Date()) {
+ logout()
+ return
+ }
+ // 直接设置状态并获取用户信息
+ userCookie.value = cookie
+ fetchUserInfo()
+ } catch {
+ logout()
+ }
+ }
+ }
+
+ return {
+ // State
+ userCookie,
+ user,
+ userInfo,
+ // Getters
+ cookieValue,
+ isLoggedIn,
+ username,
+ avatarUrl,
+ // Actions
+ login,
+ logout,
+ fetchUserInfo,
+ restoreSession
+ }
})
diff --git a/stores/users.js b/stores/users.js
new file mode 100644
index 0000000..cc04e23
--- /dev/null
+++ b/stores/users.js
@@ -0,0 +1,60 @@
+/**
+ * 用户信息缓存
+ */
+import { ref } from 'vue'
+import { usersApi } from '../api/users'
+
+// 全局缓存
+const usersInfo = ref({})
+// 请求中的 promiseMap,防止重复请求
+const inflightRequests = {}
+
+/**
+ * 根据用户ID获取用户信息(带缓存)
+ * @param {number} userID
+ * @returns {Promise<{Username, UserEmail} | null>}
+ */
+export async function fetchUserInfo(userID) {
+ if (!userID) return null
+
+ // 已有缓存
+ if (usersInfo.value[userID]) {
+ return usersInfo.value[userID]
+ }
+
+ // 请求中,等待完成
+ if (inflightRequests[userID]) {
+ return inflightRequests[userID]
+ }
+
+ // 发起请求
+ const promise = usersApi.getUserInfoFromUserID(userID).then((res) => {
+ if (res.errCode === 0 && res.raw?.return?.userinfo) {
+ const info = res.raw.return.userinfo
+ usersInfo.value[userID] = info
+ return info
+ }
+ return null
+ }).finally(() => {
+ delete inflightRequests[userID]
+ })
+
+ inflightRequests[userID] = promise
+ return promise
+}
+
+/**
+ * 根据用户ID获取用户名(同步,需先调用 fetchUserInfo)
+ * @param {number} userID
+ * @returns {string}
+ */
+export function getUsername(userID) {
+ if (!userID) return ''
+ const user = usersInfo.value[userID]
+ return user?.Username || `用户${userID}`
+}
+
+export default {
+ fetchUserInfo,
+ getUsername
+}