From 0d21742e125d204ce008a3d2a63b6d523621742e Mon Sep 17 00:00:00 2001 From: ZLI263 Date: Sun, 19 Oct 2025 13:44:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E7=94=9F=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 13 + .env | 2 + database/ai_image_analysis.sql | 32 ++ mock/dealership.js | 128 ++++++++ mock/stock.js | 171 ++++++++++ nginx.conf | 29 ++ npm-dev.service | 97 ++++++ src/api/image-management.js | 15 +- src/router/index.js | 20 +- src/views/image-model/image-list.vue | 11 +- src/views/image-model/image-to-image.vue | 397 +++++++++++++++++++++++ 11 files changed, 902 insertions(+), 13 deletions(-) create mode 100644 .dockerignore create mode 100644 .env create mode 100644 database/ai_image_analysis.sql create mode 100644 mock/dealership.js create mode 100644 mock/stock.js create mode 100644 nginx.conf create mode 100644 npm-dev.service create mode 100644 src/views/image-model/image-to-image.vue diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..575e8d3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +node_modules +npm-debug.log +.git +.gitignore +README.md +.env +.nyc_output +coverage +.DS_Store +*.log +dist +.vscode +.idea diff --git a/.env b/.env new file mode 100644 index 0000000..b68f826 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +CHOKIDAR_USEPOLLING=true +CHOKIDAR_INTERVAL=1000 diff --git a/database/ai_image_analysis.sql b/database/ai_image_analysis.sql new file mode 100644 index 0000000..8947dc9 --- /dev/null +++ b/database/ai_image_analysis.sql @@ -0,0 +1,32 @@ +-- AI图像分析服务数据库表结构 +-- 基于qwen3-vl-plus模型的图像分析请求和响应记录(合并表) + +-- 创建AI图像分析记录表(合并请求和响应) +CREATE TABLE `ai_image_analysis` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `model` varchar(50) NOT NULL COMMENT '使用的AI模型', + `image_url` varchar(500) NOT NULL COMMENT '图像URL', + `user_question` text COMMENT '用户问题文本', + `response_content` text COMMENT 'AI返回的内容', + `role` varchar(20) DEFAULT 'assistant' COMMENT '角色', + `finish_reason` varchar(20) COMMENT '完成原因', + `choice_index` int(11) DEFAULT 0 COMMENT '选择索引', + `usage_prompt_tokens` int(11) COMMENT '提示词token数量', + `usage_completion_tokens` int(11) COMMENT '完成token数量', + `usage_total_tokens` int(11) COMMENT '总token数量', + `request_data` json COMMENT '完整请求参数JSON', + `response_data` json COMMENT '完整响应数据JSON', + `user_name` varchar(100) COMMENT '用户姓名', + `user_phone` varchar(20) COMMENT '用户电话', + `session_name` varchar(200) COMMENT '会话名称', + `status` tinyint(4) DEFAULT 1 COMMENT '状态:1-成功,0-失败', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `idx_model` (`model`), + KEY `idx_status` (`status`), + KEY `idx_created_at` (`updated_at`), + KEY `idx_model_created` (`model`, `created_at`), + KEY `idx_image_url` (`image_url`(255)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='AI图像视频分析记录表'; + \ No newline at end of file diff --git a/mock/dealership.js b/mock/dealership.js new file mode 100644 index 0000000..8cdce58 --- /dev/null +++ b/mock/dealership.js @@ -0,0 +1,128 @@ +import Mock from 'mockjs' + +// 模拟经销商数据 +const dealershipData = Mock.mock({ + 'list|20': [{ + 'id|+1': 1, + 'dealershipName|1': ['销售演示-上汽', '售后演示', 'DCC演示', '销售演示-奔驰', '销售演示-比亚迪', '销售演示-宝马', '销售演示-奥迪', '销售演示-丰田', '销售演示-本田', '销售演示-日产'], + 'dealershipCode': function() { + const codes = ['XS001', 'SH001', 'DCC001', 'BC001', 'BYD001', 'BMW001', 'AUDI001', 'TOYOTA001', 'HONDA001', 'NISSAN001'] + return codes[this.id - 1] || 'DS' + Mock.Random.string('number', 3) + }, + 'remainingHours|1': [-36.31, -55.44, -4.58, 9809.16, 9959.56, 5000, -100, 2000, -50, 8000], + 'salesCount|5-20': 1, + 'avgRecordings|1-10': 1, + 'avgRecordingDuration|10-80.2-2': 1, + 'region|1': ['', '华东', '华南', '华北', '华中', '西南', '西北', '东北'], + 'groupName|1': ['', '默认', '销售组', '售后组', 'DCC组'], + 'language|1': ['普通话', '上海话', '粤语', '英语'], + 'dealershipType|1': ['售前', '售后', '销售'], + 'status|1': [true, false] + }] +}) + +// 模拟经销商列表查询 +Mock.mock(/\/dealership\/list/, 'get', (options) => { + const params = new URLSearchParams(options.url.split('?')[1]) + const current = parseInt(params.get('current')) || 1 + const size = parseInt(params.get('size')) || 20 + const dealershipName = params.get('dealershipName') + const dealershipCode = params.get('dealershipCode') + const region = params.get('region') + const status = params.get('status') + + let filteredData = dealershipData.list + + // 根据条件过滤 + if (dealershipName) { + filteredData = filteredData.filter(item => item.dealershipName.includes(dealershipName)) + } + + if (dealershipCode) { + filteredData = filteredData.filter(item => item.dealershipCode.includes(dealershipCode)) + } + + if (region) { + filteredData = filteredData.filter(item => item.region === region) + } + + if (status !== null && status !== undefined && status !== '') { + filteredData = filteredData.filter(item => item.status === (status === 'true')) + } + + const start = (current - 1) * size + const end = start + size + const pageData = filteredData.slice(start, end) + + return { + success: true, + message: '查询成功', + total: filteredData.length, + current: current, + pages: Math.ceil(filteredData.length / size), + data: pageData, + size: size + } +}) + +// 模拟添加经销商 +Mock.mock('/dealership/add', 'post', () => { + return { + success: true, + message: '经销商添加成功' + } +}) + +// 模拟更新经销商 +Mock.mock('/dealership/update', 'put', () => { + return { + success: true, + message: '经销商信息更新成功' + } +}) + +// 模拟删除经销商 +Mock.mock(/\/dealership\/delete\/\d+/, 'delete', () => { + return { + success: true, + message: '经销商删除成功' + } +}) + +// 模拟根据ID查询经销商 +Mock.mock(/\/dealership\/get\/\d+/, 'get', () => { + return { + success: true, + message: '查询成功', + data: dealershipData.list[0] + } +}) + +// 模拟根据经销商代码查询 +Mock.mock(/\/dealership\/getByCode/, 'get', () => { + return { + success: true, + message: '查询成功', + data: dealershipData.list.slice(0, 3), + count: 3 + } +}) + +// 模拟批量删除经销商 +Mock.mock('/dealership/batchDelete', 'delete', () => { + return { + success: true, + message: '批量删除成功,共删除 3 条记录' + } +}) + +// 模拟获取经销商统计信息 +Mock.mock('/dealership/statistics', 'get', () => { + return { + success: true, + message: '统计信息获取成功', + data: { + totalDealerships: 20 + } + } +}) diff --git a/mock/stock.js b/mock/stock.js new file mode 100644 index 0000000..789a3d6 --- /dev/null +++ b/mock/stock.js @@ -0,0 +1,171 @@ +import Mock from 'mockjs' + +// 模拟股票数据 +const stockData = Mock.mock({ + 'list|100': [{ + 'code': /^[0-9]{6}$/, + 'name': '@ctitle(2,4)', + 'industry|1': ['银行', '房地产', '白酒', '电子', '医药', '新能源', '化工', '钢铁', '汽车', '家电'], + 'pattern|1': ['V型反转', '强势上涨', '底部反转', '突破上涨', '双底形态', '头肩底'], + 'risePercent|20-60.1-1': 1, + 'volumeIncrease|1': ['1.5倍', '2倍', '2.5倍', '3倍', '3.5倍', '4倍'], + 'similarity|60-95.1-0': 1, + 'occurDate': '@date("yyyy-MM-dd")', + 'currentPrice|10-100.2-2': 1, + 'marketCap|100-1000.1-1': 1, + 'pe|5-50.1-1': 1, + 'pb|0.5-10.2-2': 1 + }] +}) + +// 模拟相似股票查询 +Mock.mock(/\/api\/stock\/similar/, 'get', (options) => { + const params = new URLSearchParams(options.url.split('?')[1]) + const page = parseInt(params.get('page')) || 1 + const pageSize = parseInt(params.get('pageSize')) || 20 + const patternType = params.get('patternType') + const minRise = parseInt(params.get('minRise')) || 0 + + let filteredData = stockData.list + + // 根据条件过滤 + if (patternType) { + const patternMap = { + 'v_reversal': 'V型反转', + 'strong_rise': '强势上涨', + 'bottom_reversal': '底部反转', + 'breakout_rise': '突破上涨' + } + filteredData = filteredData.filter(item => item.pattern === patternMap[patternType]) + } + + if (minRise > 0) { + filteredData = filteredData.filter(item => item.risePercent >= minRise) + } + + const start = (page - 1) * pageSize + const end = start + pageSize + const pageData = filteredData.slice(start, end) + + return { + code: 20000, + data: { + list: pageData, + total: filteredData.length, + page, + pageSize + } + } +}) + +// 模拟股票历史数据 +Mock.mock(/\/api\/stock\/history\/\d+/, 'get', () => { + const days = 100 + const data = [] + let basePrice = Mock.Random.float(10, 100, 2, 2) + + for (let i = 0; i < days; i++) { + const change = Mock.Random.float(-0.05, 0.08, 4, 4) + basePrice = basePrice * (1 + change) + + data.push({ + date: Mock.Random.date('yyyy-MM-dd'), + open: Mock.Random.float(basePrice * 0.98, basePrice * 1.02, 2, 2), + high: Mock.Random.float(basePrice * 1.01, basePrice * 1.05, 2, 2), + low: Mock.Random.float(basePrice * 0.95, basePrice * 0.99, 2, 2), + close: basePrice, + volume: Mock.Random.integer(1000000, 10000000), + amount: Mock.Random.integer(10000000, 100000000) + }) + } + + return { + code: 20000, + data: data.reverse() + } +}) + +// 模拟股票基本信息 +Mock.mock(/\/api\/stock\/info\/\d+/, 'get', () => { + return { + code: 20000, + data: { + code: Mock.Random.string('number', 6), + name: Mock.Random.ctitle(2, 4), + industry: Mock.Random.pick(['银行', '房地产', '白酒', '电子', '医药', '新能源']), + currentPrice: Mock.Random.float(10, 100, 2, 2), + change: Mock.Random.float(-5, 10, 2, 2), + changePercent: Mock.Random.float(-10, 20, 2, 2), + marketCap: Mock.Random.float(100, 1000, 1, 1), + pe: Mock.Random.float(5, 50, 1, 1), + pb: Mock.Random.float(0.5, 10, 2, 2), + turnover: Mock.Random.float(1, 10, 2, 2), + volume: Mock.Random.integer(1000000, 10000000), + amount: Mock.Random.integer(10000000, 100000000) + } + } +}) + +// 模拟行业列表 +Mock.mock('/api/stock/industries', 'get', () => { + return { + code: 20000, + data: [ + '银行', '房地产', '白酒', '电子', '医药', '新能源', '化工', '钢铁', '汽车', '家电', + '通信', '计算机', '传媒', '农林牧渔', '有色金属', '建筑材料', '机械设备', '电气设备', + '纺织服装', '轻工制造', '商业贸易', '交通运输', '公用事业', '国防军工' + ] + } +}) + +// 模拟热门股票 +Mock.mock('/api/stock/hot', 'get', () => { + return { + code: 20000, + data: Mock.mock({ + 'list|20': [{ + 'code': /^[0-9]{6}$/, + 'name': '@ctitle(2,4)', + 'currentPrice|10-100.2-2': 1, + 'changePercent|1-20.1-1': 1, + 'volume|1000000-10000000': 1 + }] + }).list + } +}) + +// 模拟自选股操作 +Mock.mock('/api/stock/watchlist', 'post', () => { + return { + code: 20000, + message: '添加成功' + } +}) + +Mock.mock('/api/stock/watchlist', 'get', () => { + return { + code: 20000, + data: Mock.mock({ + 'list|10': [{ + 'code': /^[0-9]{6}$/, + 'name': '@ctitle(2,4)', + 'currentPrice|10-100.2-2': 1, + 'changePercent|1-20.1-1': 1 + }] + }).list + } +}) + +// 模拟导出功能 +Mock.mock('/api/stock/export', 'post', () => { + return { + code: 20000, + message: '导出成功', + data: { + downloadUrl: '/api/download/stock-analysis.xlsx' + } + } +}) + + + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..e7deddf --- /dev/null +++ b/nginx.conf @@ -0,0 +1,29 @@ +server { + listen 80; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + + # 处理 Vue Router 的 history 模式 + location / { + try_files $uri $uri/ /index.html; + } + + # 静态资源缓存 + location /static/ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # 启用 gzip 压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; + + # 安全头 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; +} diff --git a/npm-dev.service b/npm-dev.service new file mode 100644 index 0000000..38384af --- /dev/null +++ b/npm-dev.service @@ -0,0 +1,97 @@ +[Unit] +Description=Vue Admin Development Server +After=network.target + +[Service] +Type=simple +User=your-username +WorkingDirectory=/path/to/your/project +ExecStart=/usr/bin/npm run dev +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/api/image-management.js b/src/api/image-management.js index 9dd42f6..a0a76c4 100644 --- a/src/api/image-management.js +++ b/src/api/image-management.js @@ -87,13 +87,26 @@ export function imageProcessing(data) { // 文本生图(基于参考图像和提示词) export function imageGenByText(data) { return request({ - url: '/image-model/image-gen-byText', + url: '/image-model/text-to-image', method: 'post', data, timeout: 60000 }) } +// 图像生图(基于源图像生成新图像) +export function imageGenByImage(formData) { + return request({ + url: '/image-model/image-to-image', + method: 'post', + data: formData, + headers: { + 'Content-Type': 'multipart/form-data' + }, + timeout: 60000 + }) +} + // 检查任务状态 export function checkImageModelStatus() { return request({ diff --git a/src/router/index.js b/src/router/index.js index f2a6071..6a79528 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -311,18 +311,18 @@ export const constantRoutes = [ component: () => import('@/views/image-model/image-list'), meta: { title: '图像管理列表', icon: 'el-icon-picture' } }, - { - path: 'poster-generation', - name: 'PosterGeneration', - component: () => import('@/views/image-model/poster-generation'), - meta: { title: '海报生成', icon: 'el-icon-picture-outline-round' } - }, { path: 'image-generation', name: 'ImageGeneration', component: () => import('@/views/image-model/image-generation'), meta: { title: '文本生图', icon: 'el-icon-picture-outline' } }, + { + path: 'image-to-image', + name: 'ImageToImage', + component: () => import('@/views/image-model/image-to-image'), + meta: { title: '图像生图', icon: 'el-icon-picture-outline' } + }, { path: 'background-model', name: 'BackgroundModel', @@ -330,10 +330,10 @@ export const constantRoutes = [ meta: { title: '图像智聚', icon: 'el-icon-picture-outline' } }, { - path: 'virtual-model', - name: 'VirtualModel', - component: () => import('@/views/image-model/virtual-model'), - meta: { title: '虚拟模特儿模型', icon: 'el-icon-user' } + path: 'smart-image-analysis', + name: 'SmartImageAnalysis', + component: () => import('@/views/video/image-video-analysis'), + meta: { title: '智解图片', icon: 'el-icon-cpu' } } ] }, diff --git a/src/views/image-model/image-list.vue b/src/views/image-model/image-list.vue index 8bd118d..f1dae14 100644 --- a/src/views/image-model/image-list.vue +++ b/src/views/image-model/image-list.vue @@ -39,7 +39,7 @@ > - + @@ -407,7 +407,10 @@ - + + + 查看成品图片 + +
+ +
+ 图像生图 +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
点击上传基础图片
+
支持 JPG、PNG、GIF 格式,大小不超过 5MB
+
+
+ 基础图片预览 +
+ 删除 +
+
+
+
+ + + + + + + + + + + + + 生成图片 + + +
+
+
+
+ + + + +