diff --git a/frontend/ops2_uniapp b/frontend/ops2_uniapp
deleted file mode 160000
index 67c9f16..0000000
--- a/frontend/ops2_uniapp
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 67c9f16301706e3f2c219f7b1617c3ffa58d0e13
diff --git a/frontend/ops_uniapp/.gitignore b/frontend/ops_uniapp/.gitignore
new file mode 100644
index 0000000..892d6b8
--- /dev/null
+++ b/frontend/ops_uniapp/.gitignore
@@ -0,0 +1,6 @@
+node_modules/
+unpackage/
+.hbuilderx/
+.DS_Store
+
+.workbuddy/
\ No newline at end of file
diff --git a/frontend/ops_uniapp/App.vue b/frontend/ops_uniapp/App.vue
new file mode 100644
index 0000000..8c2b732
--- /dev/null
+++ b/frontend/ops_uniapp/App.vue
@@ -0,0 +1,17 @@
+
+
+
diff --git a/frontend/ops_uniapp/androidPrivacy.json b/frontend/ops_uniapp/androidPrivacy.json
new file mode 100644
index 0000000..a78485c
--- /dev/null
+++ b/frontend/ops_uniapp/androidPrivacy.json
@@ -0,0 +1,3 @@
+{
+ "prompt" : "none"
+}
diff --git a/frontend/ops_uniapp/api/customer.js b/frontend/ops_uniapp/api/customer.js
new file mode 100644
index 0000000..8cfaae6
--- /dev/null
+++ b/frontend/ops_uniapp/api/customer.js
@@ -0,0 +1,28 @@
+import api from './index.js'
+
+export const customerApi = {
+ // 客户列表
+ list(params = {}) {
+ return api.post('/customer/list', params)
+ },
+
+ // 客户详情
+ get(id) {
+ return api.post('/customer/get', { id })
+ },
+
+ // 新增客户
+ add(data) {
+ return api.post('/customer/add', data)
+ },
+
+ // 编辑客户
+ update(data) {
+ return api.post('/customer/update', data)
+ },
+
+ // 删除客户
+ delete(id) {
+ return api.post('/customer/delete', { id })
+ }
+}
diff --git a/frontend/ops_uniapp/api/index.js b/frontend/ops_uniapp/api/index.js
new file mode 100644
index 0000000..fb4d81b
--- /dev/null
+++ b/frontend/ops_uniapp/api/index.js
@@ -0,0 +1,162 @@
+/**
+ * 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(path, data = {}) {
+ return request({
+ path,
+ method: 'GET',
+ data
+ })
+ },
+
+ /**
+ * POST JSON 请求(需要认证)
+ */
+ post(path, data = {}) {
+ return request({
+ path,
+ method: 'POST',
+ data // 业务数据,会被包装成 { data, userCookieValue }
+ })
+ },
+
+ /**
+ * POST FormData(文件上传)
+ */
+ 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
diff --git a/frontend/ops_uniapp/api/purchase.js b/frontend/ops_uniapp/api/purchase.js
new file mode 100644
index 0000000..17e5328
--- /dev/null
+++ b/frontend/ops_uniapp/api/purchase.js
@@ -0,0 +1,38 @@
+/**
+ * 采购订单 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)
+ },
+
+ // 更新订单
+ updateOrder(data = {}) {
+ return api.post('/purchase/updateorder', data)
+ }
+}
+
+export default purchaseApi
diff --git a/frontend/ops_uniapp/api/request.js b/frontend/ops_uniapp/api/request.js
new file mode 100644
index 0000000..d21fd71
--- /dev/null
+++ b/frontend/ops_uniapp/api/request.js
@@ -0,0 +1,4 @@
+import { useConfigStore } from '@/stores/config'
+
+const useConfig=useConfigStore()
+
diff --git a/frontend/ops_uniapp/api/schedule.js b/frontend/ops_uniapp/api/schedule.js
new file mode 100644
index 0000000..b49ecb6
--- /dev/null
+++ b/frontend/ops_uniapp/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/frontend/ops_uniapp/api/user.js b/frontend/ops_uniapp/api/user.js
new file mode 100644
index 0000000..a9656ba
--- /dev/null
+++ b/frontend/ops_uniapp/api/user.js
@@ -0,0 +1,86 @@
+/**
+ * 用户相关 API
+ */
+import api from './index'
+
+export const userApi = {
+ /**
+ * 用户登录
+ * @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/frontend/ops_uniapp/api/users.js b/frontend/ops_uniapp/api/users.js
new file mode 100644
index 0000000..19dcd18
--- /dev/null
+++ b/frontend/ops_uniapp/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/frontend/ops_uniapp/api/warehouse.js b/frontend/ops_uniapp/api/warehouse.js
new file mode 100644
index 0000000..b2a3926
--- /dev/null
+++ b/frontend/ops_uniapp/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/frontend/ops_uniapp/api/work_order.js b/frontend/ops_uniapp/api/work_order.js
new file mode 100644
index 0000000..f3b1664
--- /dev/null
+++ b/frontend/ops_uniapp/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/frontend/ops_uniapp/bump-version.js b/frontend/ops_uniapp/bump-version.js
new file mode 100644
index 0000000..dba43c6
--- /dev/null
+++ b/frontend/ops_uniapp/bump-version.js
@@ -0,0 +1,40 @@
+/**
+ * 打包前自动递增版本号
+ * 用法: node bump-version.js
+ *
+ * - versionCode: 每次 +1(必须递增,否则 Android 无法覆盖安装)
+ * - versionName: 语义化版本自动递增 patch 位(1.0.0 → 1.0.1)
+ */
+
+const fs = require('fs')
+const path = require('path')
+
+const manifestPath = path.join(__dirname, 'manifest.json')
+
+if (!fs.existsSync(manifestPath)) {
+ console.error('未找到 manifest.json,请在 ops2_uniapp 目录下运行此脚本')
+ process.exit(1)
+}
+
+// manifest.json 可能包含 JS 风格注释,先去掉再解析
+const raw = fs.readFileSync(manifestPath, 'utf8')
+const cleaned = raw.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '')
+const manifest = JSON.parse(cleaned)
+
+// 递增 versionCode
+const oldCode = parseInt(manifest.versionCode) || 0
+const newCode = oldCode + 1
+manifest.versionCode = newCode.toString()
+
+// 递增 versionName 的 patch 位
+const parts = (manifest.versionName || '1.0.0').split('.')
+if (parts.length >= 3) {
+ parts[2] = (parseInt(parts[2]) + 1).toString()
+} else {
+ parts.push('1')
+}
+manifest.versionName = parts.join('.')
+
+// 写回
+fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n')
+console.log(`版本已更新: ${manifest.versionName} (${manifest.versionCode})`)
diff --git a/frontend/ops_uniapp/components/my-toast/my-toast.vue b/frontend/ops_uniapp/components/my-toast/my-toast.vue
new file mode 100644
index 0000000..363ec9f
--- /dev/null
+++ b/frontend/ops_uniapp/components/my-toast/my-toast.vue
@@ -0,0 +1,74 @@
+
+
+ {{ message }}
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/index.html b/frontend/ops_uniapp/index.html
new file mode 100644
index 0000000..b5d330d
--- /dev/null
+++ b/frontend/ops_uniapp/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/main.js b/frontend/ops_uniapp/main.js
new file mode 100644
index 0000000..3fdb843
--- /dev/null
+++ b/frontend/ops_uniapp/main.js
@@ -0,0 +1,25 @@
+import App from './App'
+import { createPinia } from 'pinia'
+
+// #ifndef VUE3
+import Vue from 'vue'
+import './uni.promisify.adaptor'
+Vue.config.productionTip = false
+App.mpType = 'app'
+const app = new Vue({
+ ...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import { createSSRApp } from 'vue'
+export function createApp() {
+ const app = createSSRApp(App)
+ const pinia = createPinia()
+ app.use(pinia)
+ return {
+ app
+ }
+}
+// #endif
diff --git a/frontend/ops_uniapp/manifest.json b/frontend/ops_uniapp/manifest.json
new file mode 100644
index 0000000..a94819a
--- /dev/null
+++ b/frontend/ops_uniapp/manifest.json
@@ -0,0 +1,127 @@
+{
+ "name" : "Operations",
+ "appid" : "__UNI__8A0DE5E",
+ "description" : "Operations(运营)的缩写,一个前后端分离的工作流/运营管理系统。",
+ "versionName" : "1.4.3",
+ "versionCode" : "143",
+ "transformPx" : false,
+ "app-plus" : {
+ "usingComponents" : true,
+ "nvueStyleCompiler" : "uni-app",
+ "compilerVersion" : 3,
+ "splashscreen" : {
+ "alwaysShowBeforeRender" : true,
+ "waiting" : true,
+ "autoclose" : true,
+ "delay" : 0
+ },
+ "modules" : {
+ "Barcode" : {},
+ "Bluetooth" : {},
+ "Camera" : {}
+ },
+ "distribute" : {
+ "android" : {
+ "permissions" : [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ]
+ },
+ "ios" : {
+ "dSYMs" : false
+ },
+ "sdkConfigs" : {},
+ "icons" : {
+ "android" : {
+ "hdpi" : "unpackage/res/icons/72x72.png",
+ "xhdpi" : "unpackage/res/icons/96x96.png",
+ "xxhdpi" : "unpackage/res/icons/144x144.png",
+ "xxxhdpi" : "unpackage/res/icons/192x192.png"
+ },
+ "ios" : {
+ "appstore" : "unpackage/res/icons/1024x1024.png",
+ "ipad" : {
+ "app" : "unpackage/res/icons/76x76.png",
+ "app@2x" : "unpackage/res/icons/152x152.png",
+ "notification" : "unpackage/res/icons/20x20.png",
+ "notification@2x" : "unpackage/res/icons/40x40.png",
+ "proapp@2x" : "unpackage/res/icons/167x167.png",
+ "settings" : "unpackage/res/icons/29x29.png",
+ "settings@2x" : "unpackage/res/icons/58x58.png",
+ "spotlight" : "unpackage/res/icons/40x40.png",
+ "spotlight@2x" : "unpackage/res/icons/80x80.png"
+ },
+ "iphone" : {
+ "app@2x" : "unpackage/res/icons/120x120.png",
+ "app@3x" : "unpackage/res/icons/180x180.png",
+ "notification@2x" : "unpackage/res/icons/40x40.png",
+ "notification@3x" : "unpackage/res/icons/60x60.png",
+ "settings@2x" : "unpackage/res/icons/58x58.png",
+ "settings@3x" : "unpackage/res/icons/87x87.png",
+ "spotlight@2x" : "unpackage/res/icons/80x80.png",
+ "spotlight@3x" : "unpackage/res/icons/120x120.png"
+ }
+ }
+ },
+ "splashscreen" : {
+ "useOriginalMsgbox" : false
+ }
+ },
+ "nativePlugins" : {
+ "LcPrinter" : {
+ "__plugin_info__" : {
+ "name" : "LcPrinter",
+ "description" : "打印插件",
+ "platforms" : "Android",
+ "url" : "",
+ "android_package_name" : "",
+ "ios_bundle_id" : "",
+ "isCloud" : false,
+ "bought" : -1,
+ "pid" : "",
+ "parameters" : {}
+ }
+ }
+ }
+ },
+ "quickapp" : {},
+ "mp-weixin" : {
+ "appid" : "",
+ "setting" : {
+ "urlCheck" : false
+ },
+ "usingComponents" : true
+ },
+ "mp-alipay" : {
+ "usingComponents" : true
+ },
+ "mp-baidu" : {
+ "usingComponents" : true
+ },
+ "mp-toutiao" : {
+ "usingComponents" : true
+ },
+ "uniStatistics" : {
+ "enable" : false
+ },
+ "vueVersion" : "3",
+ "h5" : {
+ "devServer" : {
+ "port" : 5174
+ }
+ },
+ "locale" : "auto"
+}
diff --git a/frontend/ops_uniapp/nativeplugins/LcPrinter/android/lcprintsdk-release.aar b/frontend/ops_uniapp/nativeplugins/LcPrinter/android/lcprintsdk-release.aar
new file mode 100644
index 0000000..aaf149f
Binary files /dev/null and b/frontend/ops_uniapp/nativeplugins/LcPrinter/android/lcprintsdk-release.aar differ
diff --git a/frontend/ops_uniapp/nativeplugins/LcPrinter/android/unilcprinterplugin_module-release.aar b/frontend/ops_uniapp/nativeplugins/LcPrinter/android/unilcprinterplugin_module-release.aar
new file mode 100644
index 0000000..c328ef9
Binary files /dev/null and b/frontend/ops_uniapp/nativeplugins/LcPrinter/android/unilcprinterplugin_module-release.aar differ
diff --git a/frontend/ops_uniapp/nativeplugins/LcPrinter/package.json b/frontend/ops_uniapp/nativeplugins/LcPrinter/package.json
new file mode 100644
index 0000000..22accba
--- /dev/null
+++ b/frontend/ops_uniapp/nativeplugins/LcPrinter/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "LcPrinter",
+ "id": "LcPrinter",
+ "version": "1.0.1",
+ "description": "打印插件",
+ "_dp_type":"nativeplugin",
+ "_dp_nativeplugin":{
+ "android": {
+ "plugins": [
+ {
+ "type": "module",
+ "name": "LcPrinter",
+ "class": "uni.dcloud.io.uniplugin_lcprint.PrinterModule"
+ }
+ ],
+ "abis": [
+ "armeabi-v7a",
+ "arm64-v8a",
+ "x86"
+ ],
+ "integrateType": "aar",
+ "minSdkVersion" : 15
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/ops_uniapp/package-lock.json b/frontend/ops_uniapp/package-lock.json
new file mode 100644
index 0000000..32639ec
--- /dev/null
+++ b/frontend/ops_uniapp/package-lock.json
@@ -0,0 +1,415 @@
+{
+ "name": "ops2_uniapp",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "pinia": "^3.0.4"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
+ "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.32.tgz",
+ "integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.2",
+ "@vue/shared": "3.5.32",
+ "entities": "^7.0.1",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz",
+ "integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.32",
+ "@vue/shared": "3.5.32"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz",
+ "integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.2",
+ "@vue/compiler-core": "3.5.32",
+ "@vue/compiler-dom": "3.5.32",
+ "@vue/compiler-ssr": "3.5.32",
+ "@vue/shared": "3.5.32",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.21",
+ "postcss": "^8.5.8",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz",
+ "integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.32",
+ "@vue/shared": "3.5.32"
+ }
+ },
+ "node_modules/@vue/devtools-api": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
+ "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-kit": "^7.7.9"
+ }
+ },
+ "node_modules/@vue/devtools-kit": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
+ "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-shared": "^7.7.9",
+ "birpc": "^2.3.0",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^1.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
+ }
+ },
+ "node_modules/@vue/devtools-shared": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
+ "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
+ "license": "MIT",
+ "dependencies": {
+ "rfdc": "^1.4.1"
+ }
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.32.tgz",
+ "integrity": "sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.32"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.32.tgz",
+ "integrity": "sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.32",
+ "@vue/shared": "3.5.32"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.32.tgz",
+ "integrity": "sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.32",
+ "@vue/runtime-core": "3.5.32",
+ "@vue/shared": "3.5.32",
+ "csstype": "^3.2.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.32.tgz",
+ "integrity": "sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.32",
+ "@vue/shared": "3.5.32"
+ },
+ "peerDependencies": {
+ "vue": "3.5.32"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.32.tgz",
+ "integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==",
+ "license": "MIT"
+ },
+ "node_modules/birpc": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
+ "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/copy-anything": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
+ "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
+ "license": "MIT",
+ "dependencies": {
+ "is-what": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/hookable": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+ "license": "MIT"
+ },
+ "node_modules/is-what": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
+ "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/pinia": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
+ "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^7.7.7"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.5.0",
+ "vue": "^3.5.11"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.10",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
+ "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "license": "MIT"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/speakingurl": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
+ "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/superjson": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
+ "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
+ "license": "MIT",
+ "dependencies": {
+ "copy-anything": "^4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/vue": {
+ "version": "3.5.32",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.32.tgz",
+ "integrity": "sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.32",
+ "@vue/compiler-sfc": "3.5.32",
+ "@vue/runtime-dom": "3.5.32",
+ "@vue/server-renderer": "3.5.32",
+ "@vue/shared": "3.5.32"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/frontend/ops_uniapp/package.json b/frontend/ops_uniapp/package.json
new file mode 100644
index 0000000..f69ee64
--- /dev/null
+++ b/frontend/ops_uniapp/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "pinia": "^3.0.4"
+ }
+}
diff --git a/frontend/ops_uniapp/pages.json b/frontend/ops_uniapp/pages.json
new file mode 100644
index 0000000..9e862fd
--- /dev/null
+++ b/frontend/ops_uniapp/pages.json
@@ -0,0 +1,166 @@
+{
+ "pages": [
+ {
+ "path": "pages/index/index",
+ "style": {
+ "navigationBarTitleText": "主页"
+ }
+ },
+ {
+ "path": "pages/order/order",
+ "style": {
+ "navigationBarTitleText": "订单"
+ }
+ },
+ {
+ "path": "pages/order/order-detail",
+ "style": {
+ "navigationBarTitleText": "订单详情"
+ }
+ },
+ {
+ "path": "pages/order/order-add",
+ "style": {
+ "navigationBarTitleText": "新增订单"
+ }
+ },
+ {
+ "path": "pages/order/edit-order",
+ "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": "编辑物品"
+ }
+ },
+ {
+ "path": "pages/user/user",
+ "style": {
+ "navigationBarTitleText": "用户"
+ }
+ },
+ {
+ "path": "pages/login/login",
+ "style": {
+ "navigationBarTitleText": "登录"
+ }
+ },
+ {
+ "path": "pages/settings/settings",
+ "style": {
+ "navigationBarTitleText": "设置"
+ }
+ },
+ {
+ "path": "pages/printer-test/printer-test",
+ "style": {
+ "navigationBarTitleText": "打印机测试"
+ }
+ },
+ {
+ "path": "pages/message/message",
+ "style": {
+ "navigationBarTitleText": "消息"
+ }
+ },
+ {
+ "path": "pages/search/search",
+ "style": {
+ "navigationBarTitleText": "搜索"
+ }
+ }
+ ],
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "uni-app",
+ "navigationBarBackgroundColor": "#F8F8F8",
+ "backgroundColor": "#F8F8F8",
+ "usingComponents": {
+ "my-toast": "/components/my-toast/my-toast"
+ }
+ },
+ "tabBar": {
+ "color": "#7A7E83",
+ "selectedColor": "#007AFF",
+ "borderStyle": "black",
+ "backgroundColor": "#FFFFFF",
+ "list": [
+ {
+ "pagePath": "pages/index/index",
+ "iconPath": "static/tabbar/home.png",
+ "selectedIconPath": "static/tabbar/home-active.png",
+ "text": "主页"
+ },
+ {
+ "pagePath": "pages/order/order",
+ "iconPath": "static/tabbar/order.png",
+ "selectedIconPath": "static/tabbar/order-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",
+ "iconPath": "static/tabbar/user.png",
+ "selectedIconPath": "static/tabbar/user-active.png",
+ "text": "用户"
+ }
+ ]
+ },
+ "uniIdRouter": {}
+}
diff --git a/frontend/ops_uniapp/pages/index/index.vue b/frontend/ops_uniapp/pages/index/index.vue
new file mode 100644
index 0000000..5ea3033
--- /dev/null
+++ b/frontend/ops_uniapp/pages/index/index.vue
@@ -0,0 +1,433 @@
+
+
+
+
+
+ {{ todayDisplay }}
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+ {{ formatScheduleDate(schedule) }}
+
+
+ {{ schedule.Title }}
+ 创建人: {{ getCreatorName(schedule.UserID) }}
+
+
+
+
+
+
+ 今日暂无日程
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+ {{ pendingCount || '—' }}
+ 待处理
+
+
+ {{ arrivedCount || '—' }}
+ 已到达
+
+
+ {{ receivedCount || '—' }}
+ 已收件
+
+
+
+
+
+
+
+
+
+ 📦
+ 订单管理
+
+
+ 🏭
+ 仓库管理
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/login/login.vue b/frontend/ops_uniapp/pages/login/login.vue
new file mode 100644
index 0000000..badd15f
--- /dev/null
+++ b/frontend/ops_uniapp/pages/login/login.vue
@@ -0,0 +1,247 @@
+
+
+
+
+
+ OPS 管理系统
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ errorMsg }}
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/message/message.vue b/frontend/ops_uniapp/pages/message/message.vue
new file mode 100644
index 0000000..70cb539
--- /dev/null
+++ b/frontend/ops_uniapp/pages/message/message.vue
@@ -0,0 +1,23 @@
+
+
+ 消息
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/order/edit-order.vue b/frontend/ops_uniapp/pages/order/edit-order.vue
new file mode 100644
index 0000000..0d95de9
--- /dev/null
+++ b/frontend/ops_uniapp/pages/order/edit-order.vue
@@ -0,0 +1,762 @@
+
+
+
+
+
+
+
+
+ 基本信息
+
+
+
+ 配件名称
+
+
+
+
+
+ 备注
+
+ {{ 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/frontend/ops_uniapp/pages/order/order-add.vue b/frontend/ops_uniapp/pages/order/order-add.vue
new file mode 100644
index 0000000..2ad2362
--- /dev/null
+++ b/frontend/ops_uniapp/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/frontend/ops_uniapp/pages/order/order-detail.vue b/frontend/ops_uniapp/pages/order/order-detail.vue
new file mode 100644
index 0000000..35af1ac
--- /dev/null
+++ b/frontend/ops_uniapp/pages/order/order-detail.vue
@@ -0,0 +1,510 @@
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+ 标题
+ {{ order.Title || '-' }}
+
+
+ 链接
+ {{ order.Link }}
+
+
+ 样式
+ {{ order.Styles }}
+
+
+ 备注
+ {{ 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/frontend/ops_uniapp/pages/order/order.vue b/frontend/ops_uniapp/pages/order/order.vue
new file mode 100644
index 0000000..2cac4f9
--- /dev/null
+++ b/frontend/ops_uniapp/pages/order/order.vue
@@ -0,0 +1,433 @@
+
+
+
+
+
+
+ {{ stats.pending || 0 }}
+ 待处理
+
+
+ {{ stats.ordered || 0 }}
+ 已下单
+
+
+ {{ stats.arrived || 0 }}
+ 已到达
+
+
+ {{ stats.received || 0 }}
+ 已收件
+
+
+
+
+
+
+ 搜索
+
+
+
+ {{ currentFilter.label || '全部状态' }}
+
+
+
+
+
+
+ 加载中...
+
+
+ 暂无订单
+
+
+
+
+
+ {{ item.Title || '无标题' }}
+
+
+
+
+
+
+ 加载更多...
+
+
+ 加载更多
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/printer-test/printer-test.vue b/frontend/ops_uniapp/pages/printer-test/printer-test.vue
new file mode 100644
index 0000000..79cfd77
--- /dev/null
+++ b/frontend/ops_uniapp/pages/printer-test/printer-test.vue
@@ -0,0 +1,338 @@
+
+
+
+ 打印机控制
+
+
+ 初始化打印
+
+
+ 关闭打印
+
+
+
+
+
+ 打印测试
+
+
+ 打印小票
+
+
+ 测试打印(文字+二维码)
+
+
+ 打印标签(条形码)
+
+
+ 我的demo
+
+
+
+
+
+
+ 日志
+
+ {{ log }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/search/search.vue b/frontend/ops_uniapp/pages/search/search.vue
new file mode 100644
index 0000000..fbef6c1
--- /dev/null
+++ b/frontend/ops_uniapp/pages/search/search.vue
@@ -0,0 +1,507 @@
+
+
+
+
+ ‹
+
+ 📷
+ ×
+
+
+
+
+
+ {{ cat.label }}
+
+
+
+
+
+ 搜索中...
+
+
+
+
+ 未找到相关内容
+
+
+
+
+
+
+ {{ getIcon(item._type) }}
+
+
+ {{ getTitle(item) }}
+ {{ getDesc(item) }}
+
+ ›
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/settings/settings.vue b/frontend/ops_uniapp/pages/settings/settings.vue
new file mode 100644
index 0000000..edd3e77
--- /dev/null
+++ b/frontend/ops_uniapp/pages/settings/settings.vue
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+ base url
+
+
+
+
+ {{ useConfig.getApiBaseUrl() || '未设置' }}
+
+
+
+
+
+
+ {{ isTestok ? '✅ 连接成功' : '❌ 连接失败' }}
+
+
+
+
+
+
+ 打印机
+
+
+ 打印机测试
+ ›
+
+
+
+
+
+
+ 关于
+
+
+ 版本号
+ {{ version }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/user/user.vue b/frontend/ops_uniapp/pages/user/user.vue
new file mode 100644
index 0000000..6317fe5
--- /dev/null
+++ b/frontend/ops_uniapp/pages/user/user.vue
@@ -0,0 +1,302 @@
+
+
+
+
+
+
+
+
+ 👤
+
+ 您还未登录
+
+
+
+
+
+
+
+
+
+
+ 👤
+
+
+
+ {{ userStore.username || '未设置昵称' }}
+ {{ getUserRole() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/warehouse/add-item.vue b/frontend/ops_uniapp/pages/warehouse/add-item.vue
new file mode 100644
index 0000000..f8b0739
--- /dev/null
+++ b/frontend/ops_uniapp/pages/warehouse/add-item.vue
@@ -0,0 +1,554 @@
+
+
+
+
+
+
+
+ 名称 *
+
+
+
+
+ 编号
+
+
+
+
+ 数量
+
+ 1 && form.quantity--">
+ −
+
+
+
+ +
+
+
+
+
+
+ 备注
+
+
+
+
+
+ 关联客户
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ ×
+
+
+
+
+
+
+
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ {{ customer.primary_phone }}
+
+
+
+ 未找到匹配的客户
+
+
+
+
+
+
+ 物品图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+ 保存
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/warehouse/item-detail.vue b/frontend/ops_uniapp/pages/warehouse/item-detail.vue
new file mode 100644
index 0000000..8a0d15a
--- /dev/null
+++ b/frontend/ops_uniapp/pages/warehouse/item-detail.vue
@@ -0,0 +1,822 @@
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+ 名称
+ {{ 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) }}
+
+
+
+
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ {{ customer.title }}
+
+
+
+
+
+
+
+ 未找到物品信息
+
+
+
+
+
+
+ + 工单
+
+
+ 编辑
+
+
+ 移动
+
+
+
+
+
+
+
+
+
+ 选择容器
+
+
+ {{ selectedContainer.Title || '请选择目标容器' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/warehouse/item-edit.vue b/frontend/ops_uniapp/pages/warehouse/item-edit.vue
new file mode 100644
index 0000000..342ef1a
--- /dev/null
+++ b/frontend/ops_uniapp/pages/warehouse/item-edit.vue
@@ -0,0 +1,597 @@
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+ 名称 *
+
+
+
+
+ 编号
+
+
+
+
+ 数量
+
+ 1 && form.quantity--">
+ −
+
+
+
+ +
+
+
+
+
+
+ 备注
+
+
+
+
+
+ 关联客户
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ ×
+
+
+
+
+
+
+
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ {{ customer.primary_phone }}
+
+
+
+ 未找到匹配的客户
+
+
+
+
+
+
+ 物品图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+ 保存
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/warehouse/warehouse.vue b/frontend/ops_uniapp/pages/warehouse/warehouse.vue
new file mode 100644
index 0000000..47df8d3
--- /dev/null
+++ b/frontend/ops_uniapp/pages/warehouse/warehouse.vue
@@ -0,0 +1,1130 @@
+
+
+
+
+
+
+
+ 全部
+
+
+ 仅物品
+
+
+ 🔍
+
+
+
+
+
+
+
+ ‹ 返回上级
+
+
+ {{ crumb.title }}
+ /
+
+
+ 打印
+ ✎ 编辑
+
+
+
+ 加载中...
+
+
+ 暂无内容
+
+
+
+
+
+ 📁
+
+
+ {{ item.Title }}
+
+ 子容器: {{ item.ChildCount }} | 物品: {{ item.ItemCount }}
+
+
+ ›
+
+
+
+
+
+ 📦
+
+
+ {{ item.Name }}
+ 编号: {{ item.SerialNumber }}
+ 数量: {{ item.Quantity ?? 1 }}
+
+ ›
+
+
+
+
+
+
+
+ 加载中...
+
+
+ 暂无物品
+
+
+
+
+
+ 📦
+
+
+ {{ item.Name }}
+ 编号: {{ item.SerialNumber }}
+ 数量: {{ item.Quantity ?? 1 }}
+
+ 位置: {{ item.ContainerBreadcrumb }}
+
+
+ ›
+
+
+
+ 加载更多
+
+
+
+
+
+
+
+
+
+ +
+ 新增容器
+
+
+ +
+ 新增物品
+
+
+
+
+ +
+ 新增容器
+
+
+
+ +
+ 新增物品
+
+
+
+
+
+
+
+
+
+ 搜索
+
+
+
+
+
+
+
+
+
+
+
+ 名称 *
+
+
+
+
+ 所属目录
+
+ {{ selectedParentName }}
+ ›
+
+
+
+
+ 备注
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📁
+ {{ container.Title }}
+ ✓
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/workorder/add-workorder.vue b/frontend/ops_uniapp/pages/workorder/add-workorder.vue
new file mode 100644
index 0000000..46f851c
--- /dev/null
+++ b/frontend/ops_uniapp/pages/workorder/add-workorder.vue
@@ -0,0 +1,763 @@
+
+
+
+
+
+
+
+
+
+ 工单标题 *
+
+
+
+
+
+ 问题描述
+
+
+
+
+
+ 关联物品
+
+
+
+
+
+ {{ item.Name }}
+ {{ item.SerialNumber }}
+
+ ×
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.Name }}
+ {{ item.SerialNumber }}
+
+
+
+
+
+ 搜索中...
+
+
+
+ 未找到匹配的物品
+
+
+
+
+
+
+ 关联客户
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ ×
+
+
+
+
+
+
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ {{ customer.primary_phone }}
+
+
+
+
+ 搜索中...
+
+
+
+ 未找到匹配的客户
+
+
+
+
+
+
+ 图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+
+ 提交中...
+ 提交工单
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/workorder/edit-workorder.vue b/frontend/ops_uniapp/pages/workorder/edit-workorder.vue
new file mode 100644
index 0000000..81daca2
--- /dev/null
+++ b/frontend/ops_uniapp/pages/workorder/edit-workorder.vue
@@ -0,0 +1,888 @@
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+ {{ pageError }}
+
+
+
+
+
+
+ 工单标题 *
+
+
+
+
+
+ 问题描述
+
+
+
+
+
+ 关联物品
+
+
+
+
+
+ {{ item.Name }}
+ {{ item.SerialNumber }}
+
+ ×
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.Name }}
+ {{ item.SerialNumber }}
+
+
+
+
+
+ 搜索中...
+
+
+
+ 未找到匹配的物品
+
+
+
+
+
+
+ 关联客户
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ ×
+
+
+
+
+
+
+
+
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ {{ customer.primary_phone }}
+
+
+
+
+ 搜索中...
+
+
+
+ 未找到匹配的客户
+
+
+
+
+
+
+ 图片
+
+
+
+ ×
+
+
+ +
+ 添加图片
+
+
+
+
+
+
+
+ 保存中...
+ 保存修改
+
+
+
+
+
+
+
+
+ 确认删除
+ 确定要删除这个工单吗?此操作无法撤销。
+
+ 取消
+ 删除
+
+
+
+
+
+
+
+
+
diff --git a/frontend/ops_uniapp/pages/workorder/show-workorder.vue b/frontend/ops_uniapp/pages/workorder/show-workorder.vue
new file mode 100644
index 0000000..8c7d343
--- /dev/null
+++ b/frontend/ops_uniapp/pages/workorder/show-workorder.vue
@@ -0,0 +1,1308 @@
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+ 工单不存在
+
+
+
+
+
+
+
+
+
+ 标题
+ {{ order.Title || '-' }}
+
+
+
+ 描述
+ {{ order.Description }}
+
+
+
+
+ 关联物品
+
+
+ {{ item.Name }}
+ -{{ item.SerialNumber }}
+
+
+
+
+
+
+ 关联客户
+
+
+ {{ (customer.last_name || '') + (customer.first_name ? ' ' + customer.first_name : '') }}
+ {{ customer.primary_phone }}
+
+
+
+
+
+
+ 关联采购订单
+
+
+ #{{ 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/frontend/ops_uniapp/pages/workorder/workorder.vue b/frontend/ops_uniapp/pages/workorder/workorder.vue
new file mode 100644
index 0000000..6170270
--- /dev/null
+++ b/frontend/ops_uniapp/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/frontend/ops_uniapp/static/logo.png b/frontend/ops_uniapp/static/logo.png
new file mode 100644
index 0000000..3a22158
Binary files /dev/null and b/frontend/ops_uniapp/static/logo.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/home-active.png b/frontend/ops_uniapp/static/tabbar/home-active.png
new file mode 100644
index 0000000..13a5e7b
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/home-active.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/home.png b/frontend/ops_uniapp/static/tabbar/home.png
new file mode 100644
index 0000000..d2b6e8a
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/home.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/message-active.png b/frontend/ops_uniapp/static/tabbar/message-active.png
new file mode 100644
index 0000000..13a5e7b
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/message-active.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/message.png b/frontend/ops_uniapp/static/tabbar/message.png
new file mode 100644
index 0000000..d2b6e8a
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/message.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/order-active.png b/frontend/ops_uniapp/static/tabbar/order-active.png
new file mode 100644
index 0000000..13a5e7b
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/order-active.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/order.png b/frontend/ops_uniapp/static/tabbar/order.png
new file mode 100644
index 0000000..d2b6e8a
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/order.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/user-active.png b/frontend/ops_uniapp/static/tabbar/user-active.png
new file mode 100644
index 0000000..13a5e7b
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/user-active.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/user.png b/frontend/ops_uniapp/static/tabbar/user.png
new file mode 100644
index 0000000..d2b6e8a
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/user.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/warehouse-active.png b/frontend/ops_uniapp/static/tabbar/warehouse-active.png
new file mode 100644
index 0000000..13a5e7b
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/warehouse-active.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/warehouse.png b/frontend/ops_uniapp/static/tabbar/warehouse.png
new file mode 100644
index 0000000..d2b6e8a
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/warehouse.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/workorder-active.png b/frontend/ops_uniapp/static/tabbar/workorder-active.png
new file mode 100644
index 0000000..13a5e7b
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/workorder-active.png differ
diff --git a/frontend/ops_uniapp/static/tabbar/workorder.png b/frontend/ops_uniapp/static/tabbar/workorder.png
new file mode 100644
index 0000000..d2b6e8a
Binary files /dev/null and b/frontend/ops_uniapp/static/tabbar/workorder.png differ
diff --git a/frontend/ops_uniapp/stores/config.js b/frontend/ops_uniapp/stores/config.js
new file mode 100644
index 0000000..0569a1e
--- /dev/null
+++ b/frontend/ops_uniapp/stores/config.js
@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+
+export const useConfigStore = defineStore('config', () => {
+ // API 配置
+ const apiBaseUrl = ref('')
+
+ // 应用配置
+ const appName = ref('OPS')
+ const version = ref('1.0.0')
+
+ // 主题配置
+ const theme = ref('light')
+
+ // 设置 API 地址
+ const setApiBaseUrl = (url) => {
+ apiBaseUrl.value = url
+ uni.setStorageSync('baseUrl', url)
+ }
+
+ const getApiBaseUrl=()=>{
+ if(apiBaseUrl.value==='')
+ {
+ apiBaseUrl.value=uni.getStorageSync('baseUrl')
+ }
+ return apiBaseUrl.value
+ }
+
+ // 获取图片基础 URL(去掉末尾的 /api 部分)
+ const getFileBaseUrl = () => {
+ const base = getApiBaseUrl()
+ if (base) {
+ return base.replace(/\/api$/, '')
+ }
+ return base
+ }
+
+
+ // 设置主题
+ const setTheme = (newTheme) => {
+ theme.value = newTheme
+ }
+
+ return {
+ apiBaseUrl,
+ appName,
+ version,
+ theme,
+ setApiBaseUrl,
+ getApiBaseUrl,
+ getFileBaseUrl,
+ setTheme
+ }
+})
diff --git a/frontend/ops_uniapp/stores/user.js b/frontend/ops_uniapp/stores/user.js
new file mode 100644
index 0000000..8ea8211
--- /dev/null
+++ b/frontend/ops_uniapp/stores/user.js
@@ -0,0 +1,151 @@
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import { useConfigStore } from './config'
+
+// 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/frontend/ops_uniapp/stores/users.js b/frontend/ops_uniapp/stores/users.js
new file mode 100644
index 0000000..cc04e23
--- /dev/null
+++ b/frontend/ops_uniapp/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
+}
diff --git a/frontend/ops_uniapp/uni.promisify.adaptor.js b/frontend/ops_uniapp/uni.promisify.adaptor.js
new file mode 100644
index 0000000..5fec4f3
--- /dev/null
+++ b/frontend/ops_uniapp/uni.promisify.adaptor.js
@@ -0,0 +1,13 @@
+uni.addInterceptor({
+ returnValue (res) {
+ if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
+ return res;
+ }
+ return new Promise((resolve, reject) => {
+ res.then((res) => {
+ if (!res) return resolve(res)
+ return res[0] ? reject(res[0]) : resolve(res[1])
+ });
+ });
+ },
+});
\ No newline at end of file
diff --git a/frontend/ops_uniapp/uni.scss b/frontend/ops_uniapp/uni.scss
new file mode 100644
index 0000000..b9249e9
--- /dev/null
+++ b/frontend/ops_uniapp/uni.scss
@@ -0,0 +1,76 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:12px;
+$uni-font-size-base:14px;
+$uni-font-size-lg:16px;
+
+/* 图片尺寸 */
+$uni-img-size-sm:20px;
+$uni-img-size-base:26px;
+$uni-img-size-lg:40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:20px;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:26px;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:15px;
diff --git a/frontend/ops_uniapp/utils/index.js b/frontend/ops_uniapp/utils/index.js
new file mode 100644
index 0000000..3d771bf
--- /dev/null
+++ b/frontend/ops_uniapp/utils/index.js
@@ -0,0 +1,13 @@
+
+/**
+ * 检查字符串是否是合法的 URL
+ * @param {string} str
+ * @returns {boolean}
+ */
+export function isUrl(str) {
+ if (!str || typeof str !== 'string') return false
+
+ // 必须以 http:// 或 https:// 开头(接口地址必须带)
+ const reg = /^https?:\/\/.+/i
+ return reg.test(str.trim())
+}
\ No newline at end of file