Foods.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <template>
  2. <div class="foods admin-page">
  3. <el-card shadow="never">
  4. <div slot="header" class="admin-page-header">
  5. <span class="admin-page-title">
  6. <i class="el-icon-apple" style="color: #67c23a; margin-right: 8px;"></i>
  7. 食材管理
  8. </span>
  9. <div class="header-actions">
  10. <el-input
  11. v-model="keyword"
  12. placeholder="搜索食材名称..."
  13. prefix-icon="el-icon-search"
  14. clearable
  15. size="mini"
  16. style="width: 200px; margin-right: 8px;"
  17. @keyup.enter.native="handleSearch"
  18. @clear="handleSearch"
  19. />
  20. <el-select
  21. v-model="categoryFilter"
  22. placeholder="分类筛选"
  23. size="mini"
  24. clearable
  25. style="width: 140px; margin-right: 8px;"
  26. @change="handleSearch"
  27. >
  28. <el-option label="全部分类" value="" />
  29. <el-option v-for="opt in categoryOptions" :key="opt" :label="opt" :value="opt" />
  30. </el-select>
  31. <el-tag
  32. v-if="sortState.length > 0"
  33. closable
  34. size="mini"
  35. class="sort-tag"
  36. @close="clearSort"
  37. @click="clearSort"
  38. >
  39. {{ sortLabel }}
  40. </el-tag>
  41. <el-button type="primary" size="mini" icon="el-icon-plus" @click="openCreate">新建食材</el-button>
  42. </div>
  43. </div>
  44. <div class="adm-table-wrap">
  45. <el-table
  46. :data="list"
  47. v-loading="loading"
  48. border
  49. stripe
  50. size="mini"
  51. style="width: 100%"
  52. height="100%"
  53. >
  54. <el-table-column type="index" label="#" width="50" align="center" />
  55. <el-table-column prop="name" label="名称" min-width="120" fixed :class-name="sortClass('name')">
  56. <template slot-scope="{ row }">
  57. <span style="font-weight: 500;">{{ row.name }}</span>
  58. </template>
  59. <template slot="header">
  60. <span class="sort-header" @click.stop="onSortClick('name',$event)" @dblclick.stop="onSortToggle('name')">名称<span v-if="sortIndex('name') !== null" class="sort-index" :class="sortClass('name') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('name') }}</span>{{ sortIcon('name') }}</span>
  61. </template>
  62. </el-table-column>
  63. <el-table-column prop="category" label="分类" width="120" align="center" :class-name="sortClass('category')">
  64. <template slot-scope="{ row }">
  65. <el-tag :type="categoryTagType(row.category)" size="mini" effect="plain">
  66. {{ row.category }}
  67. </el-tag>
  68. </template>
  69. <template slot="header">
  70. <span class="sort-header" @click.stop="onSortClick('category',$event)" @dblclick.stop="onSortToggle('category')">分类<span v-if="sortIndex('category') !== null" class="sort-index" :class="sortClass('category') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('category') }}</span>{{ sortIcon('category') }}</span>
  71. </template>
  72. </el-table-column>
  73. <el-table-column label="热量" width="100" align="right" :class-name="sortClass('calories')">
  74. <template slot-scope="{ row }">
  75. <span class="num-val">{{ row.calories != null ? row.calories : '-' }}</span>
  76. <span class="num-unit">kcal</span>
  77. </template>
  78. <template slot="header">
  79. <span class="sort-header" @click.stop="onSortClick('calories',$event)" @dblclick.stop="onSortToggle('calories')">热量<span v-if="sortIndex('calories') !== null" class="sort-index" :class="sortClass('calories') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('calories') }}</span>{{ sortIcon('calories') }}</span>
  80. </template>
  81. </el-table-column>
  82. <el-table-column label="蛋白质" width="90" align="right" :class-name="sortClass('protein')">
  83. <template slot-scope="{ row }">
  84. <span class="num-val">{{ row.protein != null ? row.protein : '-' }}</span>
  85. <span class="num-unit">g</span>
  86. </template>
  87. <template slot="header">
  88. <span class="sort-header" @click.stop="onSortClick('protein',$event)" @dblclick.stop="onSortToggle('protein')">蛋白<span v-if="sortIndex('protein') !== null" class="sort-index" :class="sortClass('protein') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('protein') }}</span>{{ sortIcon('protein') }}</span>
  89. </template>
  90. </el-table-column>
  91. <el-table-column label="脂肪" width="80" align="right" :class-name="sortClass('fat')">
  92. <template slot-scope="{ row }">
  93. <span class="num-val">{{ row.fat != null ? row.fat : '-' }}</span>
  94. <span class="num-unit">g</span>
  95. </template>
  96. <template slot="header">
  97. <span class="sort-header" @click.stop="onSortClick('fat',$event)" @dblclick.stop="onSortToggle('fat')">脂肪<span v-if="sortIndex('fat') !== null" class="sort-index" :class="sortClass('fat') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('fat') }}</span>{{ sortIcon('fat') }}</span>
  98. </template>
  99. </el-table-column>
  100. <el-table-column label="碳水" width="80" align="right" :class-name="sortClass('carbs')">
  101. <template slot-scope="{ row }">
  102. <span class="num-val">{{ row.carbs != null ? row.carbs : '-' }}</span>
  103. <span class="num-unit">g</span>
  104. </template>
  105. <template slot="header">
  106. <span class="sort-header" @click.stop="onSortClick('carbs',$event)" @dblclick.stop="onSortToggle('carbs')">碳水<span v-if="sortIndex('carbs') !== null" class="sort-index" :class="sortClass('carbs') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('carbs') }}</span>{{ sortIcon('carbs') }}</span>
  107. </template>
  108. </el-table-column>
  109. <el-table-column label="膳食纤维" width="90" align="right" :class-name="sortClass('fiber')">
  110. <template slot-scope="{ row }">
  111. <span class="num-val">{{ row.fiber != null ? row.fiber : '-' }}</span>
  112. <span class="num-unit">g</span>
  113. </template>
  114. <template slot="header">
  115. <span class="sort-header" @click.stop="onSortClick('fiber',$event)" @dblclick.stop="onSortToggle('fiber')">纤维<span v-if="sortIndex('fiber') !== null" class="sort-index" :class="sortClass('fiber') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('fiber') }}</span>{{ sortIcon('fiber') }}</span>
  116. </template>
  117. </el-table-column>
  118. <el-table-column label="能量(kJ)" width="90" align="right" :class-name="sortClass('energyKj')">
  119. <template slot-scope="{ row }">
  120. <span class="num-val">{{ row.energyKj != null ? row.energyKj : '-' }}</span>
  121. </template>
  122. <template slot="header">
  123. <span class="sort-header" @click.stop="onSortClick('energyKj',$event)" @dblclick.stop="onSortToggle('energyKj')">能量<span v-if="sortIndex('energyKj') !== null" class="sort-index" :class="sortClass('energyKj') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('energyKj') }}</span>{{ sortIcon('energyKj') }}</span>
  124. </template>
  125. </el-table-column>
  126. <el-table-column label="胆固醇" width="90" align="right" :class-name="sortClass('cholesterol')">
  127. <template slot-scope="{ row }">
  128. <span v-if="row.cholesterol != null" class="num-val">{{ row.cholesterol }}<span class="num-unit">mg</span></span>
  129. <span v-else>-</span>
  130. </template>
  131. <template slot="header">
  132. <span class="sort-header" @click.stop="onSortClick('cholesterol',$event)" @dblclick.stop="onSortToggle('cholesterol')">胆固醇<span v-if="sortIndex('cholesterol') !== null" class="sort-index" :class="sortClass('cholesterol') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('cholesterol') }}</span>{{ sortIcon('cholesterol') }}</span>
  133. </template>
  134. </el-table-column>
  135. <el-table-column prop="status" label="状态" width="80" align="center" :class-name="sortClass('status')">
  136. <template slot-scope="{ row }">
  137. <el-tag :type="row.status === 'active' ? 'success' : 'info'" size="mini" effect="dark">
  138. {{ row.status === 'active' ? '启用' : '停用' }}
  139. </el-tag>
  140. </template>
  141. <template slot="header">
  142. <span class="sort-header" @click.stop="onSortClick('status',$event)" @dblclick.stop="onSortToggle('status')">状态<span v-if="sortIndex('status') !== null" class="sort-index" :class="sortClass('status') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('status') }}</span>{{ sortIcon('status') }}</span>
  143. </template>
  144. </el-table-column>
  145. <el-table-column label="操作" width="180" fixed="right" align="center">
  146. <template slot-scope="{ row }">
  147. <el-button size="mini" type="primary" plain @click="editItem(row)">编辑</el-button>
  148. <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
  149. <el-button size="mini" plain>
  150. 更多<i class="el-icon-arrow-down el-icon--right"></i>
  151. </el-button>
  152. <el-dropdown-menu slot="dropdown">
  153. <el-dropdown-item command="detail" icon="el-icon-view">查看详情</el-dropdown-item>
  154. <el-dropdown-item command="delete" icon="el-icon-delete" divided style="color: #f56c6c;">删除</el-dropdown-item>
  155. </el-dropdown-menu>
  156. </el-dropdown>
  157. </template>
  158. </el-table-column>
  159. </el-table>
  160. </div>
  161. <div class="pagination-wrap">
  162. <el-pagination
  163. @size-change="handleSizeChange"
  164. @current-change="handleCurrentChange"
  165. :current-page="page"
  166. :page-sizes="[10, 20, 50, 100]"
  167. :page-size="size"
  168. layout="total, sizes, prev, pager, next, jumper"
  169. :total="total"
  170. />
  171. </div>
  172. </el-card>
  173. <!-- 编辑/新建对话框 -->
  174. <el-dialog
  175. :title="editId ? '编辑食材' : '新建食材'"
  176. :visible.sync="dialogVisible"
  177. width="680px"
  178. :close-on-click-modal="false"
  179. >
  180. <el-form label-width="130px" size="small">
  181. <el-row :gutter="20">
  182. <el-col :span="12">
  183. <el-form-item label="名称">
  184. <el-input v-model="form.name" placeholder="食材名称" />
  185. </el-form-item>
  186. </el-col>
  187. <el-col :span="12">
  188. <el-form-item label="分类">
  189. <el-select v-model="form.category" style="width:100%" placeholder="选择分类">
  190. <el-option v-for="opt in categoryOptions" :key="opt" :label="opt" :value="opt" />
  191. </el-select>
  192. </el-form-item>
  193. </el-col>
  194. </el-row>
  195. <el-row :gutter="20">
  196. <el-col :span="12">
  197. <el-form-item label="热量(kcal/100g)">
  198. <el-input-number v-model="form.calories" :min="0" :precision="1" style="width:100%" />
  199. </el-form-item>
  200. </el-col>
  201. <el-col :span="12">
  202. <el-form-item label="能量(kJ/100g)">
  203. <el-input-number v-model="form.energyKj" :min="0" :precision="1" style="width:100%" />
  204. </el-form-item>
  205. </el-col>
  206. </el-row>
  207. <el-row :gutter="20">
  208. <el-col :span="8">
  209. <el-form-item label="蛋白质(g)">
  210. <el-input-number v-model="form.protein" :min="0" :precision="1" style="width:100%" />
  211. </el-form-item>
  212. </el-col>
  213. <el-col :span="8">
  214. <el-form-item label="碳水(g)">
  215. <el-input-number v-model="form.carbs" :min="0" :precision="1" style="width:100%" />
  216. </el-form-item>
  217. </el-col>
  218. <el-col :span="8">
  219. <el-form-item label="脂肪(g)">
  220. <el-input-number v-model="form.fat" :min="0" :precision="1" style="width:100%" />
  221. </el-form-item>
  222. </el-col>
  223. </el-row>
  224. <el-row :gutter="20">
  225. <el-col :span="8">
  226. <el-form-item label="膳食纤维(g)">
  227. <el-input-number v-model="form.fiber" :min="0" :precision="1" style="width:100%" />
  228. </el-form-item>
  229. </el-col>
  230. <el-col :span="8">
  231. <el-form-item label="淀粉(g)">
  232. <el-input-number v-model="form.starch" :min="0" :precision="1" style="width:100%" />
  233. </el-form-item>
  234. </el-col>
  235. <el-col :span="8">
  236. <el-form-item label="胆固醇(mg)">
  237. <el-input-number v-model="form.cholesterol" :min="0" :precision="1" style="width:100%" />
  238. </el-form-item>
  239. </el-col>
  240. </el-row>
  241. <el-form-item label="单位">
  242. <el-input v-model="form.unit" placeholder="如: 克、个、份" style="width: 200px;" />
  243. </el-form-item>
  244. <el-form-item label="应季月份">
  245. <el-checkbox-group v-model="form.months">
  246. <el-checkbox v-for="m in 12" :key="m" :label="m" border size="mini">{{ m }}月</el-checkbox>
  247. </el-checkbox-group>
  248. </el-form-item>
  249. <el-form-item label="状态">
  250. <el-radio-group v-model="form.status">
  251. <el-radio label="active">启用</el-radio>
  252. <el-radio label="inactive">停用</el-radio>
  253. </el-radio-group>
  254. </el-form-item>
  255. </el-form>
  256. <div slot="footer">
  257. <el-button @click="dialogVisible = false">取消</el-button>
  258. <el-button type="primary" :loading="submitting" @click="saveItem">保存</el-button>
  259. </div>
  260. </el-dialog>
  261. <!-- 详情对话框 -->
  262. <el-dialog title="食材详情" :visible.sync="detailVisible" width="500px">
  263. <el-descriptions :column="2" border size="small" v-if="detailData">
  264. <el-descriptions-item label="名称" :span="2">{{ detailData.name }}</el-descriptions-item>
  265. <el-descriptions-item label="分类">{{ detailData.category }}</el-descriptions-item>
  266. <el-descriptions-item label="状态">
  267. <el-tag :type="detailData.status === 'active' ? 'success' : 'info'" size="mini">{{ detailData.status === 'active' ? '启用' : '停用' }}</el-tag>
  268. </el-descriptions-item>
  269. <el-descriptions-item label="热量(kcal)">{{ detailData.calories || '-' }}</el-descriptions-item>
  270. <el-descriptions-item label="能量(kJ)">{{ detailData.energyKj || '-' }}</el-descriptions-item>
  271. <el-descriptions-item label="蛋白质(g)">{{ detailData.protein || '-' }}</el-descriptions-item>
  272. <el-descriptions-item label="脂肪(g)">{{ detailData.fat || '-' }}</el-descriptions-item>
  273. <el-descriptions-item label="碳水(g)">{{ detailData.carbs || '-' }}</el-descriptions-item>
  274. <el-descriptions-item label="膳食纤维(g)">{{ detailData.fiber || '-' }}</el-descriptions-item>
  275. <el-descriptions-item label="淀粉(g)">{{ detailData.starch || '-' }}</el-descriptions-item>
  276. <el-descriptions-item label="胆固醇(mg)">{{ detailData.cholesterol || '-' }}</el-descriptions-item>
  277. <el-descriptions-item label="单位">{{ detailData.unit || '-' }}</el-descriptions-item>
  278. <el-descriptions-item label="排序">{{ detailData.sortOrder != null ? detailData.sortOrder : '-' }}</el-descriptions-item>
  279. <el-descriptions-item label="应季月份" :span="2">
  280. <span v-if="detailData.months && detailData.months.length">{{ detailData.months.map(function(m) { return m + '月' }).join('、') }}</span>
  281. <span v-else style="color:#999">未设置</span>
  282. </el-descriptions-item>
  283. </el-descriptions>
  284. </el-dialog>
  285. </div>
  286. </template>
  287. <script>
  288. import { getFoodList, createFood, updateFood, deleteFood, getFoodDetail, saveFoodMonths } from '@/api/admin'
  289. export default {
  290. name: 'Foods',
  291. data() {
  292. return {
  293. submitting: false,
  294. loading: false,
  295. list: [],
  296. total: 0,
  297. page: 1,
  298. size: 20,
  299. categoryFilter: '',
  300. keyword: '',
  301. editId: null,
  302. dialogVisible: false,
  303. detailVisible: false,
  304. detailData: null,
  305. categoryOptions: [
  306. '蔬菜', '水果', '水产品', '肉类', '干果',
  307. '主食', '豆类及豆制品', '蛋类', '乳制品', '快餐', '汤'
  308. ],
  309. sortState: [],
  310. sortColLabels: {
  311. name: '\u540d\u79f0', category: '\u5206\u7c7b', calories: '\u70ed\u91cf', protein: '\u86cb\u767d',
  312. carbs: '\u78b3\u6c34', fat: '\u8102\u80aa', fiber: '\u7ea4\u7ef4', energyKj: '\u80fd\u91cf',
  313. starch: '\u6dc0\u7c89', cholesterol: '\u80c6\u56fa\u9187', status: '\u72b6\u6001'
  314. },
  315. form: {
  316. name: '',
  317. category: '',
  318. calories: null,
  319. protein: null,
  320. carbs: null,
  321. fat: null,
  322. fiber: null,
  323. energyKj: null,
  324. starch: null,
  325. cholesterol: null,
  326. unit: '克',
  327. status: 'active',
  328. months: []
  329. }
  330. }
  331. },
  332. computed: {
  333. tableHeight() {
  334. return window.innerHeight - 340
  335. },
  336. sortLabel() {
  337. if (!this.sortState || this.sortState.length === 0) return ''
  338. return this.sortState.map(s => {
  339. const col = this.sortColLabels[s.column] || s.column
  340. return s.order === 'asc' ? col + '↑' : col + '↓'
  341. }).join(' > ')
  342. }
  343. },
  344. created() {
  345. this.loadList()
  346. },
  347. methods: {
  348. async loadList() {
  349. this.loading = true
  350. try {
  351. const sortParams = { page: this.page, size: this.size }
  352. if (this.categoryFilter) sortParams.category = this.categoryFilter
  353. if (this.keyword) sortParams.keyword = this.keyword
  354. if (this.sortState.length > 0) sortParams.sort = this.sortState
  355. const res = await getFoodList(sortParams)
  356. this.list = res.data && res.data.records || []
  357. this.total = res.data && res.data.total || 0
  358. } catch (e) {
  359. this.$message && this.$message.error && this.$message.error('加载失败')
  360. } finally {
  361. this.loading = false
  362. }
  363. },
  364. handleSearch() {
  365. this.sortState = []
  366. this.loadList()
  367. },
  368. onSortClick(col, event) {
  369. const idx = this.sortState.findIndex(s => s.column === col)
  370. if (idx >= 0) {
  371. if (this.sortState[idx].order === 'asc') {
  372. this.sortState[idx].order = 'desc'
  373. } else {
  374. this.sortState.splice(idx, 1)
  375. }
  376. } else {
  377. this.sortState.push({ column: col, order: 'asc' })
  378. }
  379. this.loadList()
  380. },
  381. handleSizeChange(val) {
  382. this.size = val
  383. this.page = 1
  384. this.loadList()
  385. },
  386. handleCurrentChange(val) {
  387. this.page = val
  388. this.loadList()
  389. },
  390. openCreate() {
  391. this.editId = null
  392. this.form = { name: '', category: '', calories: null, protein: null, carbs: null, fat: null, fiber: null, energyKj: null, starch: null, cholesterol: null, unit: '克', status: 'active', months: [] }
  393. this.dialogVisible = true
  394. },
  395. async editItem(row) {
  396. this.editId = row.id
  397. this.form = {
  398. name: row.name || '',
  399. category: row.category || '',
  400. calories: row.calories,
  401. protein: row.protein,
  402. carbs: row.carbs,
  403. fat: row.fat,
  404. fiber: row.fiber,
  405. energyKj: row.energyKj,
  406. starch: row.starch,
  407. cholesterol: row.cholesterol,
  408. unit: row.unit || '克',
  409. status: row.status || 'active',
  410. months: []
  411. }
  412. // Load months for editing
  413. try {
  414. const res = await getFoodDetail(row.id)
  415. if (res.data && res.data.data && res.data.data.months) {
  416. this.form.months = res.data.data.months
  417. }
  418. } catch (e) {
  419. // months stay empty
  420. }
  421. this.dialogVisible = true
  422. },
  423. async saveItem() {
  424. this.submitting = true
  425. try {
  426. const data = { ...this.form }
  427. const months = data.months
  428. delete data.months
  429. let foodId = this.editId
  430. if (foodId) {
  431. data.id = foodId
  432. await updateFood(data)
  433. this.$message.success('更新成功')
  434. } else {
  435. const res = await createFood(data)
  436. foodId = res.data && res.data.data && res.data.data.id
  437. this.$message.success('创建成功')
  438. }
  439. // Save seasonal months
  440. if (foodId && months && months.length > 0) {
  441. try {
  442. await saveFoodMonths(foodId, months)
  443. } catch (e) {
  444. this.$message.warning('应季月份保存失败')
  445. }
  446. }
  447. this.dialogVisible = false
  448. this.loadList()
  449. } catch (e) {
  450. this.$message.error('保存失败')
  451. } finally {
  452. this.submitting = false
  453. }
  454. },
  455. deleteItem(row) {
  456. this.$confirm('确定删除 "' + row.name + '" 吗?', '提示', { type: 'warning' }).then(async () => {
  457. try {
  458. await deleteFood(row.id)
  459. this.$message.success('删除成功')
  460. this.loadList()
  461. } catch (e) {
  462. this.$message.error('删除失败')
  463. }
  464. }).catch(() => {})
  465. },
  466. showDetail(row) {
  467. this.detailData = row
  468. this.detailVisible = true
  469. },
  470. handleActionCmd(row, cmd) {
  471. switch (cmd) {
  472. case 'delete':
  473. this.deleteItem(row)
  474. break
  475. case 'detail':
  476. this.showDetail(row)
  477. break
  478. }
  479. },
  480. categoryTagType(category) {
  481. const map = {
  482. '蔬菜': 'success',
  483. '水果': 'warning',
  484. '水产品': 'primary',
  485. '肉类': 'danger',
  486. '干果': 'warning',
  487. '主食': '',
  488. '豆类及豆制品': 'success',
  489. '蛋类': 'danger',
  490. '乳制品': 'primary',
  491. '快餐': 'info',
  492. '汤': ''
  493. }
  494. return map[category] || ''
  495. }
  496. }
  497. }
  498. </script>
  499. <style scoped>
  500. .header-actions {
  501. display: flex;
  502. align-items: center;
  503. flex-wrap: wrap;
  504. gap: 4px;
  505. }
  506. .pagination-wrap {
  507. display: flex;
  508. justify-content: flex-end;
  509. padding: 16px 0 0;
  510. }
  511. .num-val {
  512. font-weight: 500;
  513. font-family: 'Helvetica Neue', Arial, sans-serif;
  514. }
  515. .num-unit {
  516. font-size: 11px;
  517. color: #999;
  518. margin-left: 2px;
  519. }
  520. .el-checkbox.is-bordered {
  521. margin-right: 6px;
  522. margin-bottom: 4px;
  523. }
  524. .el-descriptions {
  525. margin-top: 8px;
  526. }
  527. .sort-header {
  528. cursor: pointer;
  529. user-select: none;
  530. font-weight: 500;
  531. font-size: 12px;
  532. }
  533. .sort-header:hover {
  534. color: #409eff;
  535. }
  536. .sort-asc ::v-deep .el-table__header-wrapper th .cell,
  537. .sort-desc ::v-deep .el-table__header-wrapper th .cell {
  538. color: #409eff;
  539. font-weight: 600;
  540. }
  541. .sort-tag {
  542. margin-left: 8px;
  543. cursor: pointer;
  544. }
  545. </style>