index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. <template>
  2. <div class="carousel-page">
  3. <div class="carousel-container">
  4. <!-- 搜索区域(独立卡片) -->
  5. <div class="search-card">
  6. <el-form ref="queryFormRef" :model="queryParams" :inline="true">
  7. <el-form-item label="广告名称" prop="title">
  8. <el-input v-model="queryParams.title" placeholder="请输入广告名称" clearable @keyup.enter="handleQuery" />
  9. </el-form-item>
  10. <el-form-item label="起始时间" prop="dateRange">
  11. <el-date-picker
  12. v-model="dateRange"
  13. type="daterange"
  14. range-separator="-"
  15. start-placeholder="开始时间"
  16. end-placeholder="结束时间"
  17. value-format="YYYY-MM-DD"
  18. style="width: 240px"
  19. />
  20. </el-form-item>
  21. <el-form-item>
  22. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  23. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  24. <el-button type="primary" plain icon="Plus" @click="handleAdd">添加广告</el-button>
  25. </el-form-item>
  26. </el-form>
  27. <div class="toolbar">
  28. <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
  29. </div>
  30. </div>
  31. <!-- 表格区域(独立卡片) -->
  32. <div class="table-card">
  33. <!-- 表格标题 -->
  34. <div class="table-title">轮播广告信息列表</div>
  35. <!-- 表格 -->
  36. <el-table v-loading="loading" :data="carouselList" border>
  37. <el-table-column label="广告图片" align="center" width="120">
  38. <template #default="scope">
  39. <el-image
  40. :src="scope.row.imageUrl"
  41. :preview-src-list="[scope.row.imageUrl]"
  42. fit="cover"
  43. style="width: 80px; height: 60px; border-radius: 4px"
  44. lazy
  45. />
  46. </template>
  47. </el-table-column>
  48. <el-table-column label="广告名称" align="center" prop="title" :show-overflow-tooltip="true" min-width="200" />
  49. <el-table-column label="时间" align="center" width="280">
  50. <template #default="scope">
  51. <div>开始时间:{{ scope.row.startTime?.substring(0, 10) }}</div>
  52. <div>到期时间:{{ scope.row.endTime?.substring(0, 10) }}</div>
  53. </template>
  54. </el-table-column>
  55. <el-table-column label="上线/下线" align="center" width="100">
  56. <template #default="scope">
  57. <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
  58. {{ scope.row.status === 1 ? '上线' : '下线' }}
  59. </el-tag>
  60. </template>
  61. </el-table-column>
  62. <el-table-column label="排序" align="center" prop="sort" width="80" />
  63. <el-table-column label="链接" align="center" prop="link" :show-overflow-tooltip="true" min-width="200" />
  64. <el-table-column label="操作" align="center" width="150">
  65. <template #default="scope">
  66. <span class="action-link primary" @click="handleUpdate(scope.row)">编辑</span>
  67. <span class="action-link danger" @click="handleDelete(scope.row)">删除</span>
  68. </template>
  69. </el-table-column>
  70. </el-table>
  71. <!-- 分页 -->
  72. <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
  73. </div>
  74. </div>
  75. <!-- 添加或修改对话框 -->
  76. <el-dialog v-model="dialog.visible" :title="dialog.title" width="620px" append-to-body @close="cancel">
  77. <el-form ref="carouselFormRef" :model="form" :rules="rules" label-width="100px">
  78. <el-row :gutter="20">
  79. <el-col :span="12">
  80. <el-form-item label="广告名称" prop="title">
  81. <el-input v-model="form.title" placeholder="请输入广告名称" />
  82. </el-form-item>
  83. </el-col>
  84. <el-col :span="12">
  85. <el-form-item label="上线/下线" prop="status">
  86. <el-radio-group v-model="form.status">
  87. <el-radio :value="1">上线</el-radio>
  88. <el-radio :value="0">下线</el-radio>
  89. </el-radio-group>
  90. </el-form-item>
  91. </el-col>
  92. </el-row>
  93. <el-row :gutter="20">
  94. <el-col :span="12">
  95. <el-form-item label="开始时间" prop="startTime">
  96. <el-date-picker
  97. v-model="form.startTime"
  98. type="date"
  99. placeholder="请选择"
  100. value-format="YYYY-MM-DD"
  101. style="width: 100%"
  102. @change="form.endTime = ''"
  103. />
  104. </el-form-item>
  105. </el-col>
  106. <el-col :span="12">
  107. <el-form-item label="到期时间" prop="endTime">
  108. <el-date-picker
  109. v-model="form.endTime"
  110. type="date"
  111. placeholder="请选择"
  112. value-format="YYYY-MM-DD"
  113. style="width: 100%"
  114. :disabled-date="disabledEndDate"
  115. />
  116. </el-form-item>
  117. </el-col>
  118. </el-row>
  119. <el-row :gutter="20">
  120. <el-col :span="12">
  121. <el-form-item label="背景色" prop="color">
  122. <el-color-picker v-model="form.color" show-alpha />
  123. </el-form-item>
  124. </el-col>
  125. <el-col :span="12">
  126. <el-form-item label="排序" prop="sort">
  127. <el-input-number v-model="form.sort" :min="0" controls-position="right" />
  128. </el-form-item>
  129. </el-col>
  130. </el-row>
  131. <el-form-item label="链接" prop="link">
  132. <el-input v-model="form.link" placeholder="请输入链接" />
  133. </el-form-item>
  134. <el-form-item label="图片" prop="imageUrl">
  135. <upload-image v-model="form.imageUrl" :limit="1" />
  136. </el-form-item>
  137. <el-form-item label="描述" prop="remark">
  138. <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入内容" />
  139. </el-form-item>
  140. </el-form>
  141. <template #footer>
  142. <div class="dialog-footer">
  143. <el-button type="primary" @click="submitForm">确 认</el-button>
  144. <el-button @click="cancel">取 消</el-button>
  145. </div>
  146. </template>
  147. </el-dialog>
  148. </div>
  149. </template>
  150. <script setup name="GiftCarousel" lang="ts">
  151. import { getCurrentInstance, onMounted, reactive, ref } from 'vue';
  152. import type { ComponentInternalInstance } from 'vue';
  153. import type { ElFormInstance } from 'element-plus';
  154. import dayjs from 'dayjs';
  155. import { listAdContent, addAdContent, updateAdContent, delAdContent } from '@/api/ad/content';
  156. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  157. const carouselList = ref<any[]>([]);
  158. const loading = ref(false);
  159. const total = ref(0);
  160. const showSearch = ref(true);
  161. const dateRange = ref<[string, string]>(['', '']);
  162. const queryParams = ref({
  163. pageNum: 1,
  164. pageSize: 10,
  165. title: '',
  166. adType: 'gift_banner',
  167. startTime: '',
  168. endTime: ''
  169. });
  170. const dialog = reactive<DialogOption>({
  171. visible: false,
  172. title: ''
  173. });
  174. const carouselFormRef = ref<ElFormInstance>();
  175. const initFormData = {
  176. id: undefined as number | undefined,
  177. adType: 'gift_banner',
  178. title: '',
  179. imageUrl: '',
  180. startTime: '',
  181. endTime: '',
  182. link: '',
  183. color: '#ffffff',
  184. remark: '',
  185. extJson: '{}',
  186. sort: 0,
  187. status: 1
  188. };
  189. const form = ref({ ...initFormData });
  190. const disabledEndDate = (date: Date) => {
  191. if (!form.value.startTime) return false;
  192. const start = new Date(form.value.startTime + 'T00:00:00');
  193. return date.getTime() < start.getTime();
  194. };
  195. const rules = reactive({
  196. title: [{ required: true, message: '广告名称不能为空', trigger: 'blur' }],
  197. startTime: [{ required: true, message: '开始时间不能为空', trigger: 'change' }],
  198. endTime: [{ required: true, message: '结束时间不能为空', trigger: 'change' }]
  199. });
  200. /** 查询列表 */
  201. const getList = async () => {
  202. loading.value = true;
  203. const params: any = {
  204. pageNum: queryParams.value.pageNum,
  205. pageSize: queryParams.value.pageSize,
  206. title: queryParams.value.title,
  207. adType: queryParams.value.adType
  208. };
  209. if (dateRange.value && dateRange.value[0] && dateRange.value[1]) {
  210. params.startTime = dateRange.value[0];
  211. params.endTime = dateRange.value[1];
  212. }
  213. const res = await listAdContent(params);
  214. carouselList.value = res.rows || [];
  215. total.value = res.total || 0;
  216. loading.value = false;
  217. };
  218. /** 搜索按钮操作 */
  219. const handleQuery = () => {
  220. queryParams.value.pageNum = 1;
  221. getList();
  222. };
  223. /** 重置按钮操作 */
  224. const resetQuery = () => {
  225. dateRange.value = ['', ''];
  226. queryParams.value.title = '';
  227. queryParams.value.startTime = '';
  228. queryParams.value.endTime = '';
  229. handleQuery();
  230. };
  231. /** 取消按钮 */
  232. const cancel = () => {
  233. reset();
  234. dialog.visible = false;
  235. };
  236. /** 表单重置 */
  237. const reset = () => {
  238. form.value = { ...initFormData };
  239. carouselFormRef.value?.resetFields();
  240. };
  241. /** 新增按钮操作 */
  242. const handleAdd = () => {
  243. reset();
  244. const today = dayjs().format('YYYY-MM-DD');
  245. const future = dayjs().add(30, 'day').format('YYYY-MM-DD');
  246. form.value.startTime = today;
  247. form.value.endTime = future;
  248. dialog.visible = true;
  249. dialog.title = '添加广告';
  250. };
  251. /** 修改按钮操作 */
  252. const handleUpdate = (row: any) => {
  253. reset();
  254. form.value = {
  255. ...row,
  256. adType: 'gift_banner',
  257. imageUrl: row.imageUrl || '',
  258. status: row.status ?? 1,
  259. color: row.color || '#ffffff',
  260. remark: row.remark || '',
  261. extJson: row.extJson || '{}',
  262. startTime: row.startTime ? dayjs(row.startTime).format('YYYY-MM-DD') : '',
  263. endTime: row.endTime ? dayjs(row.endTime).format('YYYY-MM-DD') : ''
  264. };
  265. dialog.visible = true;
  266. dialog.title = '修改广告';
  267. };
  268. /** 提交按钮 */
  269. const submitForm = () => {
  270. carouselFormRef.value?.validate((valid: boolean) => {
  271. if (valid) {
  272. const api = form.value.id ? updateAdContent : addAdContent;
  273. api(form.value).then(() => {
  274. proxy?.$modal.msgSuccess(form.value.id ? '修改成功' : '新增成功');
  275. dialog.visible = false;
  276. getList();
  277. });
  278. }
  279. });
  280. };
  281. /** 删除按钮操作 */
  282. const handleDelete = (row: any) => {
  283. proxy?.$modal.confirm('是否确认删除广告名称为"' + row.title + '"的数据项?').then(() => {
  284. delAdContent(row.id).then(() => {
  285. proxy?.$modal.msgSuccess('删除成功');
  286. getList();
  287. });
  288. });
  289. };
  290. onMounted(() => {
  291. getList();
  292. });
  293. </script>
  294. <style scoped lang="scss">
  295. .carousel-page {
  296. min-height: 100vh;
  297. background: #f5f5f5;
  298. padding: 20px;
  299. }
  300. .carousel-container {
  301. max-width: 1200px;
  302. margin: 0 auto;
  303. }
  304. .search-card {
  305. background: #fff;
  306. border-radius: 4px;
  307. padding: 20px;
  308. margin-bottom: 12px;
  309. display: flex;
  310. justify-content: space-between;
  311. align-items: flex-start;
  312. .el-form {
  313. flex: 1;
  314. }
  315. .toolbar {
  316. flex-shrink: 0;
  317. }
  318. }
  319. .table-card {
  320. background: #fff;
  321. border-radius: 4px;
  322. padding: 20px;
  323. }
  324. .table-title {
  325. font-size: 16px;
  326. font-weight: 600;
  327. color: #303133;
  328. margin-bottom: 15px;
  329. }
  330. .action-link {
  331. cursor: pointer;
  332. margin: 0 8px;
  333. &.primary {
  334. color: #409eff;
  335. &:hover {
  336. color: #66b1ff;
  337. }
  338. }
  339. &.danger {
  340. color: #f56c6c;
  341. &:hover {
  342. color: #f78989;
  343. }
  344. }
  345. }
  346. </style>