From cd24728b34f86f552b0270791f26fdbe9051b994 Mon Sep 17 00:00:00 2001 From: zxl <763096477@qq.com> Date: 星期五, 15 八月 2025 15:28:33 +0800 Subject: [PATCH] 抽奖活动 --- manager/src/api/activity-prize.js | 35 ++ manager/src/views/activity-prize/index.vue | 830 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 865 insertions(+), 0 deletions(-) diff --git a/manager/src/api/activity-prize.js b/manager/src/api/activity-prize.js new file mode 100644 index 0000000..e0bfcab --- /dev/null +++ b/manager/src/api/activity-prize.js @@ -0,0 +1,35 @@ +import service from "../libs/axios"; + +export const getPage = (params) =>{ + return service({ + url: "/lmk/activity-prize", + method: "GET", + params: params + }) +} +export const detail = (params) =>{ + return service({ + url: "/lmk/activity-prize/" +params, + method: "GET", + }) +} +export const edit = (params) =>{ + return service({ + url: "/lmk/activity-prize", + method: "PUT", + data: params + }) +} +export const add = (params) =>{ + return service({ + url: "/lmk/activity-prize", + method: "POST", + data: params + }) +} +export const del = (params) =>{ + return service({ + url: "/lmk/activity-prize/" +params, + method: "DELETE", + }) +} diff --git a/manager/src/views/activity-prize/index.vue b/manager/src/views/activity-prize/index.vue new file mode 100644 index 0000000..d29b7aa --- /dev/null +++ b/manager/src/views/activity-prize/index.vue @@ -0,0 +1,830 @@ +<template> + <div> + <Card> + <!-- 鎼滅储琛ㄥ崟 --> + <Form + ref="searchForm" + @keydown.enter.native="handleSearch" + :model="searchForm" + inline + :label-width="80" + class="search-form" + > + <FormItem label="娲诲姩鍚嶇О" prop="activityName"> + <Input + type="text" + v-model="searchForm.activityName" + placeholder="璇疯緭鍏ユ椿鍔ㄥ悕绉�" + clearable + @on-clear="handleSearch" + style="width: 180px" + /> + </FormItem> + <FormItem label="鎶ュ悕寮�濮嬫椂闂�" prop="beginTime"> + <DatePicker + :value="searchForm.beginTime" + type="datetime" + placeholder="閫夋嫨寮�濮嬫椂闂�" + style="width: 180px" + value-format="YYYY-MM-DD HH:mm:ss" + @on-change="handleSearch('beginTime', $event)" + @on-clear="handleSearch" + ></DatePicker> + </FormItem> + <FormItem label="鎶ュ悕缁撴潫鏃堕棿" prop="endTime"> + <DatePicker + value-format="YYYY-MM-DD HH:mm:ss" + :value="searchForm.endTime" + type="datetime" + placeholder="閫夋嫨缁撴潫鏃堕棿" + style="width: 180px" + @on-clear="handleSearch" + @on-change="handleSearch('endTime', $event)" + ></DatePicker> + </FormItem> + <Button + @click="handleSearch" + type="primary" + icon="ios-search" + class="search-btn" + >鎼滅储</Button> + <Button + @click="resetSearch" + icon="md-refresh" + style="margin-left: 8px" + >閲嶇疆</Button> + </Form> + + <!-- 鎿嶄綔鎸夐挳 --> + <Row class="operation"> + <Button @click="openAdd" type="primary" icon="md-add">鏂板鎶藉娲诲姩</Button> + </Row> + + <!-- 娲诲姩琛ㄦ牸 --> + <Table + :loading="loading" + border + :columns="columns" + :data="activityList" + ref="table" + class="activity-table" + > + <!-- 鎿嶄綔鎸夐挳鎻掓Ы --> + <template slot-scope="{ row }" slot="action"> + <div class="action-btns"> + <Button + type="primary" + size="small" + @click="changeStatus(row, row.enableStatus === 'on' ? '鍏抽棴' : '寮�鍚�')" + :loading="row.statusLoading" + > + {{row.enableStatus === 'on' ? '鍏抽棴' : '寮�鍚�'}} + </Button> + <Button + type="info" + size="small" + @click="detail(row)" + >璇︽儏</Button> + <Button + type="info" + size="small" + @click="setPrize(row)" + >濂栧搧璁剧疆</Button> + <Button + type="info" + size="small" + @click="openEdit(row)" + >缂栬緫</Button> + <Button + type="error" + size="small" + @click="delById(row)" + >鍒犻櫎</Button> + + </div> + </template> + </Table> + + <!-- 鍒嗛〉 --> + <Row type="flex" justify="end" class="page-footer"> + <Page + :current="searchForm.pageNumber" + :total="total" + :page-size="searchForm.pageSize" + @on-change="changePage" + @on-page-size-change="changePageSize" + :page-size-opts="[10, 20, 50]" + size="small" + show-total + show-elevator + show-sizer + ></Page> + </Row> + + <!-- 娲诲姩缂栬緫/鏂板妯℃�佹 --> + <Modal + v-model="modelShow" + :title="modelTitle" + @on-cancel="modelClose" + width="800" + :mask-closable="false" + > + <Form ref="form" :model="activityFrom" :label-width="100" :rules="rules" > + <Row :gutter="16"> + <Col span="12"> + <FormItem label="娲诲姩鍚嶇О" prop="activityName"> + <Input + v-model="activityFrom.activityName" + placeholder="璇疯緭鍏ユ椿鍔ㄥ悕绉�" + clearable + /> + </FormItem> + </Col> + <Col span="12"> + <FormItem label="娲诲姩鎻忚堪" prop="activityDes" :label-width="100"> + <Input v-model="activityFrom.activityDes" type="textarea" :autosize="{minRows: 2,maxRows: 5}" placeholder="璇疯緭鍏�"></Input> + </FormItem> + </Col> + <Col span="12"> + <FormItem label="寮�濮嬫椂闂�" prop="beginTime"> + <DatePicker + v-model="activityFrom.beginTime" + type="datetime" + placeholder="閫夋嫨寮�濮嬫椂闂�" + style="width: 180px" + ></DatePicker> + </FormItem> + </Col> + <Col span="12"> + <FormItem label="缁撴潫鏃堕棿" prop="endTime"> + <DatePicker + v-model="activityFrom.endTime" + type="datetime" + placeholder="璇烽�夋嫨缁撴潫鏃堕棿" + style="width: 180px" + ></DatePicker> + </FormItem> + </Col> + <Col span="12"> + <FormItem label="姣忔棩鏈�澶ф娊濂栦笂闄�" prop="maxPrize"> + <InputNumber + v-model="activityFrom.maxPrize" + :min="1" + placeholder="璇疯緭鍏ユ瘡鏃ユ渶澶ф娊濂栦笂闄�" + style="width: 180px" + /> + </FormItem> + </Col> + <Col span="12"> + <FormItem label="鍒濆鍖栨娊濂栨鏁�" prop="prizeNum" > + <InputNumber + :min="1" + v-model="activityFrom.prizeNum" + placeholder="璇疯緭鍏ュ垵濮嬪寲鎶藉娆℃暟" + style="width: 180px" + /> + </FormItem> + </Col> + <Col span="24"> + <FormItem label="娲诲姩鍥剧墖锛�" prop="activityImg"> + <Upload + :before-upload="(file) => handleBeforeUpload(file, 'content')" + :format="['jpg','jpeg','png','gif']" + :max-size="20480" + action="" + accept="image/*" + > + <Button icon="ios-cloud-upload-outline">涓婁紶灏侀潰鍥剧墖</Button> + <div class="upload-tip">鏀寔鍥剧墖锛屾渶澶�20MB</div> + </Upload> + <div v-if="activityImg" class="upload-file-info"> + <p>宸查�夋枃浠�: {{ activityImg.name }}</p> + <img :src="activityImg.activityImgUrl" alt="娲诲姩鍥剧墖" style="max-width: 100%; max-height: 200px;"> + <Button type="text" @click="handleRemove('content')">鍒犻櫎</Button> + </div> + + <!-- 鍩轰簬elementUi鐨勪笂浼犵粍浠� el-upload end--> + </FormItem> + </Col> + <Col span="24"> + <FormItem label="娲诲姩灏侀潰锛�" prop="activityCover"> + <Upload + :before-upload="(file) => handleBeforeUpload(file, 'cover')" + :format="['jpg','jpeg','png','gif']" + :max-size="20480" + action="" + accept="image/*" + > + <Button icon="ios-cloud-upload-outline">涓婁紶灏侀潰鍥剧墖</Button> + <div class="upload-tip">鏀寔鍥剧墖锛屾渶澶�20MB</div> + </Upload> + <div v-if="activityCover" class="upload-file-info"> + <p>宸查�夋枃浠�: {{ activityCover.name }}</p> + <img :src="activityCover.activityCoverUrl" alt="娲诲姩鍥剧墖" style="max-width: 100%; max-height: 200px;"> + <Button type="text" @click="handleRemove('cover')">鍒犻櫎</Button> + </div> + + <!-- 鍩轰簬elementUi鐨勪笂浼犵粍浠� el-upload end--> + </FormItem> + </Col> + </Row> + </Form> + + <div slot="footer"> + <Button @click="modelClose">鍙栨秷</Button> + <Button type="primary" :loading="submitLoading" @click="saveOrUpdate">鎻愪氦</Button> + </div> + </Modal> + <!-- 璇︽儏--> + <Modal + v-model="infoModalShow" + title="娲诲姩璇︽儏" + @on-cancel="infoModelClose" + width="800" + :mask-closable="false" + > + <div class="detail-container"> + <Row :gutter="16"> + <Col span="12"> + <div class="detail-item"> + <label>娲诲姩鍚嶇О锛�</label> + <span>{{ detailData.activityName }}</span> + </div> + </Col> + <Col span="12"> + <div class="detail-item"> + <label>娲诲姩鎻忚堪锛�</label> + <span>{{ detailData.activityDes}}</span> + </div> + </Col> + <Col span="12"> + <div class="detail-item"> + <label>寮�濮嬫椂闂达細</label> + <span>{{ formatDate(detailData.beginTime) }}</span> + </div> + </Col> + <Col span="12"> + <div class="detail-item"> + <label>缁撴潫鏃堕棿锛�</label> + <span>{{ formatDate(detailData.endTime) }}</span> + </div> + </Col> + <Col span="12"> + <div class="detail-item"> + <label>姣忔棩鏈�澶ф娊濂栦笂闄愶細</label> + <span>{{ detailData.maxPrize || '0' }}</span> + </div> + </Col> + <Col span="12"> + <div class="detail-item"> + <label>鍒濆鍖栨娊濂栨鏁帮細</label> + <span>{{ detailData.prizeNum || '0' }}</span> + </div> + </Col> + <Col span="24"> + <div class="detail-item"> + <label>娲诲姩鐘舵�侊細</label> + <Tag :color="detailData.enableStatus === 'on' ? 'success' : 'default'"> + {{ detailData.enableStatus === 'on' ? '宸插惎鐢�' : '鏈惎鐢�' }} + </Tag> + </div> + </Col> + <Col span="24"> + <div class="detail-item"> + <label>娲诲姩鍥剧墖锛�</label> + <div v-if="detailData.activityImg" class="detail-image"> + <img :src="detailData.activityImg" alt="娲诲姩鍥剧墖" style="max-width: 100%; max-height: 200px;"> + </div> + <span v-else>-</span> + </div> + </Col> + <Col span="24"> + <div class="detail-item"> + <label>娲诲姩灏侀潰锛�</label> + <div v-if="detailData.activityCover" class="detail-image"> + <img + :src="detailData.activityCover" alt="娲诲姩灏侀潰" + style="max-width: 100%; max-height: 200px;" + > + </div> + <span v-else>-</span> + </div> + </Col> + <Col span="24"> + <div class="detail-item"> + <label>濂栧搧锛�</label> + </div> + </Col> + </Row> + </div> + + </Modal> + </Card> + </div> +</template> + +<script> +import { + getPage, + detail, + edit, + add, + del +} from '@/api/activity-prize.js' +import {delByKey, uploadFileByLmk} from "../../api/common"; + +export default { + name: "activityPrize", + + data() { + return { + infoModalShow:false, + detailData: {}, + modelShow:false, + modelTitle:'', + rules: { + activityDes:[ + { required: true, message: '璇疯緭鍏ユ椿鍔ㄦ弿杩�', trigger: 'blur' }, + { max: 100, message: '闀垮害涓嶈兘瓒呰繃100涓瓧绗�', trigger: 'blur' } + ], + activityName: [ + { required: true, message: '璇疯緭鍏ユ椿鍔ㄥ悕绉�', trigger: 'blur' }, + { max: 50, message: '闀垮害涓嶈兘瓒呰繃50涓瓧绗�', trigger: 'blur' } + ], + beginTime: [ + { type:'date',required: true, message: '璇烽�夋嫨寮�濮嬫椂闂�', trigger: 'change'}, + { + trigger: ['change' ], + validator: this.validateBeginTime + } + ], + endTime: [ + { type:'date',required: true, message: '璇烽�夋嫨缁撴潫鏃堕棿', trigger: 'change'}, + + { trigger: ['change'] , + validator: this.validateEndTime + } + ], + maxPrize: [ + {required: true, type: 'number', message: '璇疯緭鍏ユ瘡鏃ユ渶澶ф娊濂栦笂闄�', trigger: 'blur'}, + {type: 'number', min: 1, message: '蹇呴』澶т簬0', trigger: 'blur'} + ], + prizeNum: [ + {required: true, type: 'number', message: '璇疯緭鍏ュ垵濮嬪寲鎶藉娆℃暟', trigger: 'blur'}, + {type: 'number', min: 1, message: '蹇呴』澶т簬0', trigger: 'blur'} + ], + activityCover: [ + {required: true, message: '璇烽�夋嫨娲诲姩灏侀潰', trigger: 'blur'} + ], + activityImg: [ + {required: true, message: '璇烽�夋嫨娲诲姩鍥剧墖', trigger: 'blur'} + ], + }, + activityFrom:{ + id:'', + activityName:'', + activityDes:'', + beginTime:'', + endTime:'', + maxPrize:0, + prizeNum:0, + activityImg:'', + activityCover:'', + enableStatus:'off' + }, + loading:false, + columns:[ + { + title: '娲诲姩鍚嶇О', + key: 'activityName', + minWidth: 100, + }, + { + title: '娲诲姩鎻忚堪', + key: 'activityDes', + minWidth: 100, + }, + { + title: '寮�濮嬫椂闂�', + key: 'beginTime', + minWidth: 100, + render: (h, params) => { + return h('div', [ + h('div', this.formatDate(params.row.beginTime)), + ]) + } + }, + { + title: '缁撴潫鏃堕棿', + key: 'endTime', + minWidth: 100, + render: (h, params) => { + return h('div', [ + h('div', this.formatDate(params.row.endTime)), + ]) + } + }, + { + title: '姣忔棩鏈�澶ф娊濂栦笂闄�', + key: 'maxPrize', + minWidth: 100, + }, + { + title: '鍒濆鍖栨娊濂栨鏁�', + key: 'prizeNum', + minWidth: 100, + }, + { + title: '鏄惁寮�鍚椿鍔�', + key: 'enableStatus', + minWidth: 100, + render: (h, params) => { + return h('Tag', { + props: { + color: params.row.enableStatus === 'on' ? 'green' : 'default' + } + }, params.row.enableStatus === 'on' ? '寮�鍚�' : '鍏抽棴') + } + }, + { + title: '鎿嶄綔', + slot: 'action', + width: 280, + align: 'center', + fixed: 'right' + }, + ], + activityList:[], + total:0, + searchForm:{ + pageSize:10, + pageNumber:1, + activityName:'', + endTime:'', + beginTime:'', + + }, + submitLoading:false, + activityCover:null, + activityImg:null, + } + }, + // 鍦ㄧ粍浠跺垱寤哄墠娉ㄥ唽 + beforeCreate() { + + }, + mounted() { + this.init(); + }, + methods: { + validateBeginTime(rule, value, callback) { + console.log("瑙﹀彂楠岃瘉") + // 鏍¢獙蹇呭~ + if (!value) { + return callback(new Error('璇烽�夋嫨寮�濮嬫椂闂�')); // 鉁� 姝g‘鎻愮ず + } + console.log("琛ㄥ崟寮�濮嬮獙璇佸紑濮嬫椂闂�") + console.log(value) + console.log(this.activityFrom.beginTime) + // 鏍¢獙寮�濮嬫椂闂翠笉鑳芥櫄浜庣粨鏉熸椂闂� + if (new Date(this.activityFrom.beginTime) > new Date(this.activityFrom.endTime) ) { + return callback(new Error('寮�濮嬫椂闂翠笉鑳芥櫄浜庣粨鏉熸椂闂�')); + } + callback(); // 楠岃瘉閫氳繃 + }, + validateEndTime(rule, value, callback) { + // 鏍¢獙蹇呭~ + if (!value) { + return callback(new Error('璇烽�夋嫨缁撴潫鏃堕棿')); // 鉁� 姝g‘鎻愮ず + } + // 鏍¢獙缁撴潫鏃堕棿涓嶈兘鏃╀簬寮�濮嬫椂闂� + if (new Date(this.activityFrom.endTime) < new Date(this.activityFrom.beginTime)) { + return callback(new Error('缁撴潫鏃堕棿涓嶈兘鏃╀簬寮�濮嬫椂闂�')); + } + callback(); // 楠岃瘉閫氳繃 + }, + setPrize(row){ + + }, + formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') { + if (!date) return ''; + + const d = new Date(date); + if (isNaN(d.getTime())) return ''; + + const padZero = (num) => String(num).padStart(2, '0'); // 鏇村彲闈犵殑琛ラ浂鏂规硶 + + const year = d.getFullYear(); + const month = padZero(d.getMonth() + 1); // 鏈堜唤 0-11 鈫� +1 + const day = padZero(d.getDate()); + const hours = padZero(d.getHours()); + const minutes = padZero(d.getMinutes()); + const seconds = padZero(d.getSeconds()); + return format + .replace('YYYY', year) + .replace('MM', month) + .replace('DD', day) + .replace('HH', hours) + .replace('mm', minutes) + .replace('ss', seconds); + }, + resetSearch(){ + this.$refs.searchForm.resetFields() + this.searchForm.pageNumber = 1 + this.getPage() + }, + handleBeforeUpload(file,type){ + if ("content" === type){ + this.activityImg = file + this.submitLoading = true + const formData = new FormData() + formData.append('file', this.activityImg) + + uploadFileByLmk(formData).then(res => { + this.submitLoading = false + if (res.code === 200) { + this.activityFrom.activityImg = res.data.fileKey; + this.activityImg.activityImgUrl = res.data.url; + this.$Message.success('涓婁紶鎴愬姛') + }else{ + this.$Message.error(res.msg) + } + }).catch(() => { + this.submitLoading = false + }) + }else if ("cover" === type){ + this.activityCover = file + this.submitLoading = true + const formData = new FormData() + formData.append('file', this.activityCover) + + uploadFileByLmk(formData).then(res => { + this.submitLoading = false + if (res.code === 200) { + this.activityFrom.activityCover = res.data.fileKey + this.activityCover.activityCoverUrl = res.data.url; + this.$Message.success('涓婁紶鎴愬姛') + }else{ + this.$Message.error(res.msg) + } + }).catch(() => { + this.submitLoading = false + }) + } + + + }, + // 鍒犻櫎鏂囦欢 + handleRemove(type) { + if ("content" === type){ + //鐐瑰嚮鍏抽棴绐楀彛鏃剁‘淇濇枃浠跺凡琚竻闄� + if (this.activityImg === null) { + return; + } + if (!this.activityFrom.activityImg) { + this.activityImg = null + return + } + delByKey(this.activityFrom.activityImg).then(res => { + if (res.code === 200) { + this.activityImg = null + this.activityFrom.activityImg = '' + }else{ + this.$Message.error(res.msg) + } + }) + }else if ("cover" === type){ + if (this.activityCover === null) { + return; + } + if (!this.activityFrom.activityCover) { + this.activityCover = null + return + } + delByKey(this.activityFrom.activityCover).then(res => { + if (res.code === 200) { + this.activityCover = null + this.activityFrom.activityCover = '' + }else{ + this.$Message.error(res.msg) + } + }) + } + + + + }, + saveOrUpdate(){ + const submitData = { + ...this.activityFrom, + beginTime: this.formatDate(this.activityFrom.beginTime, 'YYYY-MM-DD HH:mm:ss'), + endTime: this.formatDate(this.activityFrom.endTime, 'YYYY-MM-DD HH:mm:ss') + }; + console.log(submitData) + this.$refs.form.validate(valid => { + if (valid) { + this.submitLoading = true + const api = this.activityFrom.id ? edit : add + api(submitData).then(res => { + this.submitLoading = false + if (res.code === 200) { + this.$Message.success(res.msg) + this.modelClose() + this.getPage() + }else{ + this.$Message.error(res.msg) + } + }).catch(() => { + this.submitLoading = false + }) + } + }) + }, + infoModelClose(){ + this.infoModalShow = false; + }, + modelClose(){ + this.modelShow = false + this.submitLoading = false + this.handleRemove("content"); + this.handleRemove("cover"); + this.$refs.form.resetFields() + }, + getPage(){ + this.loading = true; + getPage(this.searchForm).then(res =>{ + this.loading = false; + if (res.code === 200){ + this.activityList = res.data; + }else { + this.$Message.error(res.msg) + } + }) + }, + detail(row){ + this.infoModalShow = true; + this.detailData = {...row}; + }, + openEdit(row){ + this.modelShow = true; + this.modelTitle = "缂栬緫娲诲姩"; + this.activityFrom= { + id:row.id, + activityName:row.activityName, + activityDes:row.activityDes, + beginTime:this.formatDate(row.beginTime, 'YYYY-MM-DD HH:mm:ss'), + endTime: this.formatDate(row.endTime, 'YYYY-MM-DD HH:mm:ss'), + maxPrize:row.maxPrize, + prizeNum:row.prizeNum, + activityImg:row.activityImg, + activityCover:row.activityCover, + enableStatus:row.enableStatus, + } + }, + + handleSearch(type,value){ + if (type === 'beginTime') { + this.searchForm.beginTime = value + } else if (type === 'endTime') { + this.searchForm.endTime = value + } + this.getPage() + }, + openAdd(){ + this.modelShow = true; + this.modelTitle = "鏂板娲诲姩"; + }, + delById(row){ + del(row.id).then(res=>{ + if (res.code === 200){ + this.$Message.success(res.msg) + }else { + this.$Message.error(res.msg) + } + this.getPage() + }) + }, + // 鑾峰彇瀵屾枃鏈紪杈戝櫒鐨勫唴瀹� + // 鍒濆鍖栨暟鎹� + init() { + this.getPage() + }, + changePage(){ + this.searchForm.pageNumber = 1 + this.searchForm.pageSize = pageSize + this.getPage() + }, + changePageSize(){ + this.searchForm.pageNumber = page + this.getPage() + }, + changeStatus(){ + + }, + + }, +} +</script> + +<style lang="scss" scoped> +.detail-container { + padding: 16px; +} + +.detail-item { + margin-bottom: 16px; + line-height: 1.5; +} + +.detail-item label { + display: inline-block; + width: 120px; + font-weight: bold; + color: #515a6e; + vertical-align: top; +} + +.detail-item span { + display: inline-block; + max-width: calc(100% - 120px); +} + +.detail-image { + display: inline-block; + margin-top: 8px; + border: 1px dashed #dcdee2; + padding: 4px; + border-radius: 4px; + background: #f8f8f9; +} +.search-form { + padding: 16px; + background: #f8f8f9; + border-radius: 4px; + margin-bottom: 16px; + + .ivu-form-item { + margin-bottom: 16px; + margin-right: 16px; + } + + .search-btn { + margin-left: 8px; + } +} +.operation { + margin-bottom: 16px; + + .ivu-btn { + margin-right: 8px; + } +} +.activity-table { + .media-container { + display: flex; + justify-content: center; + align-items: center; + height: 100px; + + .thumbnail { + max-width: 100%; + max-height: 100%; + object-fit: contain; + cursor: pointer; + transition: all 0.3s; + + &:hover { + transform: scale(1.05); + box-shadow: 0 0 8px rgba(0, 0, 0, 0.2); + } + } + + .video-player { + max-width: 100%; + max-height: 100px; + background: #000; + } + + .text-cover { + padding: 8px; + background: #f8f8f9; + border-radius: 4px; + max-width: 100%; + word-break: break-all; + } + } + + .action-btns { + display: flex; + flex-wrap: wrap; + justify-content: center; + + .ivu-btn { + margin: 4px; + font-size: 12px; + padding: 2px 6px; + min-width: 60px; + } + } +} +</style> -- Gitblit v1.8.0