Category.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. <template>
  2. <div class="food-category">
  3. <el-card shadow="hover">
  4. <template #header>
  5. <div class="card-header">
  6. <span>食品数据管理</span>
  7. </div>
  8. </template>
  9. <!-- 搜索和筛选区域 -->
  10. <div class="search-filter">
  11. <el-form :model="searchForm" label-width="80px" inline>
  12. <el-form-item label="食品名称">
  13. <el-input
  14. v-model="searchForm.productName"
  15. placeholder="请输入食品名称"
  16. clearable
  17. @keyup.enter="getProductList"
  18. />
  19. </el-form-item>
  20. <el-form-item label="食品类型">
  21. <el-select
  22. v-model="searchForm.foodType"
  23. placeholder="请选择食品类型"
  24. clearable
  25. >
  26. <el-option label="蔬菜" value="vegetable" />
  27. <el-option label="水果" value="fruit" />
  28. <el-option label="肉类" value="meat" />
  29. <el-option label="海鲜" value="seafood" />
  30. <el-option label="加工食品" value="processed" />
  31. </el-select>
  32. </el-form-item>
  33. <el-form-item label="生成状态">
  34. <el-select
  35. v-model="searchForm.status"
  36. placeholder="请选择状态"
  37. clearable
  38. >
  39. <el-option label="已生成" value="success" />
  40. <el-option label="生成中" value="generating" />
  41. <el-option label="生成失败" value="failed" />
  42. </el-select>
  43. </el-form-item>
  44. <el-form-item>
  45. <el-button type="primary" @click="getProductList">搜索</el-button>
  46. <el-button @click="resetSearch">重置</el-button>
  47. </el-form-item>
  48. </el-form>
  49. </div>
  50. <!-- 产品列表 -->
  51. <div class="food-list">
  52. <el-table
  53. :data="productList"
  54. style="width: 100%"
  55. stripe
  56. @row-click="handleRowClick"
  57. >
  58. <el-table-column prop="id" label="食品ID" width="80" />
  59. <el-table-column prop="productName" label="食品名称" width="200" />
  60. <el-table-column prop="foodType" label="食品类型" width="120">
  61. <template #default="scope">
  62. <el-tag
  63. :type="scope.row.foodType === 'vegetable' ? 'success' :
  64. scope.row.foodType === 'fruit' ? 'warning' :
  65. scope.row.foodType === 'meat' ? 'danger' :
  66. scope.row.foodType === 'seafood' ? 'primary' : 'info'"
  67. >
  68. {{ scope.row.foodType === 'vegetable' ? '蔬菜' :
  69. scope.row.foodType === 'fruit' ? '水果' :
  70. scope.row.foodType === 'meat' ? '肉类' :
  71. scope.row.foodType === 'seafood' ? '海鲜' : '加工食品' }}
  72. </el-tag>
  73. </template>
  74. </el-table-column>
  75. <el-table-column prop="size" label="图片尺寸" width="120" />
  76. <el-table-column prop="style" label="设计风格" width="120" />
  77. <el-table-column prop="status" label="生成状态" width="120">
  78. <template #default="scope">
  79. <el-tag
  80. :type="scope.row.status === 'success' ? 'success' :
  81. scope.row.status === 'generating' ? 'warning' : 'danger'"
  82. >
  83. {{ scope.row.status === 'success' ? '已生成' :
  84. scope.row.status === 'generating' ? '生成中' : '生成失败' }}
  85. </el-tag>
  86. </template>
  87. </el-table-column>
  88. <el-table-column prop="createTime" label="创建时间" width="180" />
  89. <el-table-column label="操作" width="200">
  90. <template #default="scope">
  91. <el-button type="primary" size="small" @click="viewProduct(scope.row)">查看详情</el-button>
  92. <el-button type="danger" size="small" @click="deleteProduct(scope.row.id)">删除</el-button>
  93. </template>
  94. </el-table-column>
  95. </el-table>
  96. <!-- 分页 -->
  97. <div class="pagination">
  98. <el-pagination
  99. @size-change="handleSizeChange"
  100. @current-change="handleCurrentChange"
  101. :current-page="pagination.currentPage"
  102. :page-sizes="[10, 20, 50, 100]"
  103. :page-size="pagination.pageSize"
  104. layout="total, sizes, prev, pager, next, jumper"
  105. :total="pagination.total"
  106. />
  107. </div>
  108. </div>
  109. </el-card>
  110. <!-- 产品详情对话框 -->
  111. <el-dialog
  112. v-model="detailDialogVisible"
  113. title="食品详情"
  114. width="800px"
  115. :before-close="handleDetailClose"
  116. >
  117. <div class="food-detail" v-if="selectedProduct">
  118. <div class="detail-section">
  119. <h3>基本信息</h3>
  120. <el-descriptions :column="2" border>
  121. <el-descriptions-item label="食品ID">{{ selectedProduct.id }}</el-descriptions-item>
  122. <el-descriptions-item label="食品名称">{{ selectedProduct.productName }}</el-descriptions-item>
  123. <el-descriptions-item label="食品类型">
  124. <el-tag
  125. :type="selectedProduct.foodType === 'vegetable' ? 'success' :
  126. selectedProduct.foodType === 'fruit' ? 'warning' :
  127. selectedProduct.foodType === 'meat' ? 'danger' :
  128. selectedProduct.foodType === 'seafood' ? 'primary' : 'info'"
  129. >
  130. {{ selectedProduct.foodType === 'vegetable' ? '蔬菜' :
  131. selectedProduct.foodType === 'fruit' ? '水果' :
  132. selectedProduct.foodType === 'meat' ? '肉类' :
  133. selectedProduct.foodType === 'seafood' ? '海鲜' : '加工食品' }}
  134. </el-tag>
  135. </el-descriptions-item>
  136. <el-descriptions-item label="产地">{{ selectedProduct.origin }}</el-descriptions-item>
  137. <el-descriptions-item label="营养价值">{{ selectedProduct.nutrition }}</el-descriptions-item>
  138. <el-descriptions-item label="图片尺寸">{{ selectedProduct.size }}</el-descriptions-item>
  139. <el-descriptions-item label="设计风格">{{ selectedProduct.style }}</el-descriptions-item>
  140. <el-descriptions-item label="生成状态">
  141. <el-tag
  142. :type="selectedProduct.status === 'success' ? 'success' :
  143. selectedProduct.status === 'generating' ? 'warning' : 'danger'"
  144. >
  145. {{ selectedProduct.status === 'success' ? '已生成' :
  146. selectedProduct.status === 'generating' ? '生成中' : '生成失败' }}
  147. </el-tag>
  148. </el-descriptions-item>
  149. <el-descriptions-item label="创建时间" :span="2">{{ selectedProduct.createTime }}</el-descriptions-item>
  150. </el-descriptions>
  151. </div>
  152. <div class="detail-section">
  153. <h3>提示词信息</h3>
  154. <el-input
  155. v-model="selectedProduct.prompt"
  156. type="textarea"
  157. :rows="4"
  158. readonly
  159. style="background-color: #f5f7fa"
  160. />
  161. </div>
  162. <div class="detail-section">
  163. <h3>生成结果</h3>
  164. <div class="generated-image">
  165. <el-image
  166. v-if="selectedProduct.generatedImage"
  167. :src="selectedProduct.generatedImage"
  168. fit="contain"
  169. style="max-width: 100%; max-height: 400px"
  170. >
  171. <template #error>
  172. <div class="image-error">图片加载失败</div>
  173. </template>
  174. </el-image>
  175. <el-empty v-else description="暂无生成图片" />
  176. </div>
  177. </div>
  178. </div>
  179. </el-dialog>
  180. </div>
  181. </template>
  182. <script setup>
  183. import {ref,reactive,onMounted} from 'vue'
  184. import {ElMessage,ElMessageBox,ElEmpty,ElImage} from 'element-plus'
  185. import {Loading} from '@element-plus/icons-vue'
  186. import {useUserStore} from '@/pinia/modules/user'
  187. // 假设存在获取产品列表的API,需要根据实际情况修改
  188. // import {getProductListApi, deleteProductApi} from '@/api/mes/product'
  189. // 定义组件名称
  190. defineOptions({name: 'Category'})
  191. // 搜索表单
  192. const searchForm = reactive({
  193. productName: '',
  194. foodType: '',
  195. status: ''
  196. })
  197. // 食品列表数据
  198. const productList = ref([])
  199. // 分页信息
  200. const pagination = reactive({
  201. currentPage: 1,
  202. pageSize: 10,
  203. total: 0
  204. })
  205. // 加载状态
  206. const loading = ref(false)
  207. // 详情对话框
  208. const detailDialogVisible = ref(false)
  209. const selectedProduct = ref(null)
  210. // 模拟食品数据
  211. const mockProducts = [
  212. {
  213. id: 1,
  214. productName: '有机胡萝卜',
  215. foodType: 'vegetable',
  216. origin: '山东寿光',
  217. nutrition: '富含胡萝卜素、维生素A',
  218. size: '1024x1024',
  219. style: 'realistic',
  220. status: 'success',
  221. createTime: '2023-10-20 10:30:00',
  222. prompt: '创建一张有机胡萝卜的宣传图片,展示新鲜的有机胡萝卜,背景使用自然土壤色调,突出健康、有机的主题。',
  223. generatedImage: 'https://picsum.photos/seed/carrot1/1024/1024'
  224. },
  225. {
  226. id: 2,
  227. productName: '挪威三文鱼',
  228. foodType: 'seafood',
  229. origin: '挪威',
  230. nutrition: '富含Omega-3脂肪酸、蛋白质',
  231. size: '1024x1024',
  232. style: 'realistic',
  233. status: 'success',
  234. createTime: '2023-10-21 11:45:00',
  235. prompt: '设计一张挪威三文鱼的宣传海报,展示新鲜的三文鱼片,背景使用海洋元素,突出其新鲜和营养价值。',
  236. generatedImage: 'https://picsum.photos/seed/salmon1/1024/1024'
  237. },
  238. {
  239. id: 3,
  240. productName: '原味鱼干',
  241. foodType: 'processed',
  242. origin: '福建厦门',
  243. nutrition: '富含蛋白质、钙、磷',
  244. size: '1024x1024',
  245. style: 'realistic',
  246. status: 'success',
  247. createTime: '2023-10-23 09:00:00',
  248. prompt: '创建一张原味鱼干的宣传图片,展示自然晾晒的鱼干,背景使用海边场景,突出其新鲜和传统工艺。',
  249. generatedImage: 'https://images.unsplash.com/photo-1610486295123-078299c50e79?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1024&q=80'
  250. }
  251. ]
  252. // 获取食品列表
  253. const getProductList = async () => {
  254. loading.value = true
  255. try {
  256. // 实际项目中这里应该调用API获取数据
  257. // const res = await getProductListApi({
  258. // page: pagination.currentPage,
  259. // pageSize: pagination.pageSize,
  260. // productName: searchForm.productName,
  261. // foodType: searchForm.foodType,
  262. // status: searchForm.status
  263. // })
  264. // 模拟API调用
  265. await new Promise(resolve => setTimeout(resolve, 500))
  266. // 模拟筛选数据
  267. let filteredProducts = [...mockProducts]
  268. if (searchForm.productName) {
  269. filteredProducts = filteredProducts.filter(p => p.productName.includes(searchForm.productName))
  270. }
  271. if (searchForm.foodType) {
  272. filteredProducts = filteredProducts.filter(p => p.foodType === searchForm.foodType)
  273. }
  274. if (searchForm.status) {
  275. filteredProducts = filteredProducts.filter(p => p.status === searchForm.status)
  276. }
  277. productList.value = filteredProducts
  278. pagination.total = filteredProducts.length
  279. ElMessage({ message: '获取食品列表成功', type: 'success' })
  280. } catch (error) {
  281. console.error('获取食品列表失败:', error)
  282. ElMessage({ message: '获取食品列表失败', type: 'error' })
  283. } finally {
  284. loading.value = false
  285. }
  286. }
  287. // 重置搜索
  288. const resetSearch = () => {
  289. searchForm.productName = ''
  290. searchForm.foodType = ''
  291. searchForm.status = ''
  292. pagination.currentPage = 1
  293. getProductList()
  294. }
  295. // 分页处理
  296. const handleSizeChange = (size) => {
  297. pagination.pageSize = size
  298. pagination.currentPage = 1
  299. getProductList()
  300. }
  301. const handleCurrentChange = (current) => {
  302. pagination.currentPage = current
  303. getProductList()
  304. }
  305. // 查看产品详情
  306. const viewProduct = (product) => {
  307. selectedProduct.value = {...product}
  308. detailDialogVisible.value = true
  309. }
  310. // 处理行点击
  311. const handleRowClick = (row) => {
  312. viewProduct(row)
  313. }
  314. // 删除产品
  315. const deleteProduct = (id) => {
  316. ElMessageBox.confirm('确定要删除该产品吗?', '提示', {
  317. confirmButtonText: '确定',
  318. cancelButtonText: '取消',
  319. type: 'warning'
  320. }).then(async () => {
  321. try {
  322. // 实际项目中这里应该调用API删除数据
  323. // await deleteProductApi(id)
  324. // 模拟API调用
  325. await new Promise(resolve => setTimeout(resolve, 300))
  326. // 从列表中移除
  327. const index = productList.value.findIndex(p => p.id === id)
  328. if (index > -1) {
  329. productList.value.splice(index, 1)
  330. pagination.total--
  331. }
  332. ElMessage({ message: '删除成功', type: 'success' })
  333. } catch (error) {
  334. console.error('删除失败:', error)
  335. ElMessage({ message: '删除失败', type: 'error' })
  336. }
  337. }).catch(() => {
  338. // 取消删除
  339. })
  340. }
  341. // 处理详情对话框关闭
  342. const handleDetailClose = () => {
  343. detailDialogVisible.value = false
  344. selectedProduct.value = null
  345. }
  346. // 页面挂载时获取产品列表
  347. onMounted(() => {
  348. getProductList()
  349. })
  350. </script>
  351. <style scoped>
  352. .product-category {
  353. padding: 20px;
  354. }
  355. .card-header {
  356. display: flex;
  357. justify-content: space-between;
  358. align-items: center;
  359. }
  360. .search-filter {
  361. margin-bottom: 20px;
  362. }
  363. .product-list {
  364. margin-top: 20px;
  365. }
  366. .pagination {
  367. margin-top: 20px;
  368. text-align: right;
  369. }
  370. .product-detail {
  371. padding: 10px 0;
  372. }
  373. .detail-section {
  374. margin-bottom: 25px;
  375. }
  376. .detail-section h3 {
  377. margin-bottom: 15px;
  378. font-size: 16px;
  379. font-weight: 600;
  380. color: #303133;
  381. border-bottom: 1px solid #ebeef5;
  382. padding-bottom: 5px;
  383. }
  384. .generated-image {
  385. margin-top: 10px;
  386. padding: 20px;
  387. background-color: #fafafa;
  388. border-radius: 8px;
  389. text-align: center;
  390. }
  391. .image-error {
  392. display: flex;
  393. justify-content: center;
  394. align-items: center;
  395. width: 100%;
  396. height: 200px;
  397. background-color: #f5f7fa;
  398. color: #909399;
  399. }
  400. </style>