Honors.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <template>
  2. <div>
  3. <div class="header-actions">
  4. <h2>荣誉资质管理</h2>
  5. <el-button type="primary" @click="openDialog()">新增</el-button>
  6. </div>
  7. <el-table :data="items" style="width: 100%" v-loading="loading">
  8. <el-table-column prop="id" label="ID" width="80" />
  9. <el-table-column prop="title" label="标题" />
  10. <el-table-column label="图片">
  11. <template #default="scope">
  12. <el-image style="width: 100px; height: 60px" :src="getImageUrl(scope.row.image_url)" fit="cover" />
  13. </template>
  14. </el-table-column>
  15. <el-table-column label="操作" width="180">
  16. <template #default="scope">
  17. <el-button size="small" @click="openDialog(scope.row)">编辑</el-button>
  18. <el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
  19. </template>
  20. </el-table-column>
  21. </el-table>
  22. <el-dialog v-model="dialogVisible" :title="isEdit ? '编辑荣誉' : '新增荣誉'">
  23. <el-form :model="form" label-width="120px">
  24. <el-form-item label="标题">
  25. <el-input v-model="form.title" />
  26. </el-form-item>
  27. <el-form-item label="图片">
  28. <el-upload
  29. class="avatar-uploader"
  30. :http-request="customUpload"
  31. :show-file-list="false"
  32. :on-success="handleUploadSuccess"
  33. >
  34. <img v-if="form.image_url" :src="getImageUrl(form.image_url)" class="avatar" />
  35. <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
  36. </el-upload>
  37. </el-form-item>
  38. </el-form>
  39. <template #footer>
  40. <span class="dialog-footer">
  41. <el-button @click="dialogVisible = false">取消</el-button>
  42. <el-button type="primary" @click="handleSubmit">确定</el-button>
  43. </span>
  44. </template>
  45. </el-dialog>
  46. </div>
  47. </template>
  48. <script setup>
  49. import { ref, onMounted, computed } from 'vue'
  50. import api from '../api'
  51. import { useAuthStore } from '../stores/auth'
  52. import { ElMessage, ElMessageBox } from 'element-plus'
  53. const items = ref([])
  54. const loading = ref(false)
  55. const dialogVisible = ref(false)
  56. const isEdit = ref(false)
  57. const form = ref({
  58. id: null,
  59. title: '',
  60. image_url: ''
  61. })
  62. const authStore = useAuthStore()
  63. const uploadUrl = import.meta.env.VITE_UPLOAD_URL
  64. const customUpload = async (options) => {
  65. const { file, onSuccess, onError } = options
  66. const formData = new FormData()
  67. formData.append('file', file)
  68. try {
  69. const res = await api.post('/upload', formData, {
  70. headers: {
  71. 'Content-Type': 'multipart/form-data'
  72. }
  73. })
  74. onSuccess(res.data)
  75. } catch (error) {
  76. onError(error)
  77. ElMessage.error('上传失败')
  78. }
  79. }
  80. const getImageUrl = (url) => {
  81. if (!url) return ''
  82. if (url.startsWith('http')) return url
  83. return `${import.meta.env.VITE_IMG_BASE_URL}${url}`
  84. }
  85. const fetchData = async () => {
  86. loading.value = true
  87. try {
  88. const res = await api.get('/honors')
  89. items.value = res.data
  90. } catch (error) {
  91. ElMessage.error('获取数据失败')
  92. } finally {
  93. loading.value = false
  94. }
  95. }
  96. const openDialog = (item = null) => {
  97. if (item) {
  98. isEdit.value = true
  99. form.value = { ...item }
  100. } else {
  101. isEdit.value = false
  102. form.value = { id: null, title: '', image_url: '' }
  103. }
  104. dialogVisible.value = true
  105. }
  106. const handleUploadSuccess = (response) => {
  107. form.value.image_url = response.url
  108. }
  109. const handleSubmit = async () => {
  110. try {
  111. if (isEdit.value) {
  112. await api.put(`/honors/${form.value.id}`, form.value)
  113. ElMessage.success('更新成功')
  114. } else {
  115. await api.post('/honors', form.value)
  116. ElMessage.success('创建成功')
  117. }
  118. dialogVisible.value = false
  119. fetchData()
  120. } catch (error) {
  121. ElMessage.error('操作失败')
  122. }
  123. }
  124. const handleDelete = (item) => {
  125. ElMessageBox.confirm('确定要删除此荣誉吗?', '警告', {
  126. confirmButtonText: '是',
  127. cancelButtonText: '否',
  128. type: 'warning'
  129. }).then(async () => {
  130. try {
  131. await api.delete(`/honors/${item.id}`)
  132. ElMessage.success('删除成功')
  133. fetchData()
  134. } catch (error) {
  135. ElMessage.error('删除失败')
  136. }
  137. })
  138. }
  139. onMounted(fetchData)
  140. </script>
  141. <style scoped>
  142. .header-actions {
  143. display: flex;
  144. justify-content: space-between;
  145. align-items: center;
  146. margin-bottom: 20px;
  147. }
  148. .avatar-uploader .el-upload {
  149. border: 1px dashed #d9d9d9;
  150. border-radius: 6px;
  151. cursor: pointer;
  152. position: relative;
  153. overflow: hidden;
  154. }
  155. .avatar-uploader .el-upload:hover {
  156. border-color: #409eff;
  157. }
  158. .avatar-uploader-icon {
  159. font-size: 28px;
  160. color: #8c939d;
  161. width: 178px;
  162. height: 178px;
  163. text-align: center;
  164. line-height: 178px;
  165. }
  166. .avatar {
  167. width: 178px;
  168. height: 178px;
  169. display: block;
  170. }
  171. </style>