poster-edit.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <!-- 合伙人推广海报编辑 -->
  2. <template>
  3. <el-dialog
  4. :title="editForm.id ? '编辑海报' : '新增海报'"
  5. v-model="visible"
  6. width="1000px"
  7. @closed="resetForm"
  8. >
  9. <div class="poster-edit-container">
  10. <!-- 左侧手机预览 -->
  11. <div class="mobile-preview">
  12. <div class="preview-content">
  13. <div class="preview-poster" v-if="editForm.background">
  14. <el-image
  15. :src="editForm.background"
  16. fit="cover"
  17. style="width: 250px; height: 445px"
  18. />
  19. <!-- 头像预览 -->
  20. <div
  21. class="avatar-preview"
  22. v-if="editForm.headImgPosX && editForm.headImgPosY"
  23. :style="{
  24. left: editForm.headImgPosX + 'px',
  25. top: editForm.headImgPosY + 'px',
  26. width: '30px',
  27. height: '30px',
  28. borderRadius: '50%',
  29. background: '#e0e0e0',
  30. position: 'absolute'
  31. }"
  32. ></div>
  33. <!-- 昵称预览 -->
  34. <div
  35. class="nickname-preview"
  36. v-if="
  37. editForm.nickNamePosX && editForm.nickNamePosY
  38. "
  39. :style="{
  40. left: editForm.nickNamePosX + 'px',
  41. top: editForm.nickNamePosY + 'px',
  42. position: 'absolute',
  43. color: '#333',
  44. fontSize: '14px'
  45. }"
  46. >微信昵称</div
  47. >
  48. <!-- 二维码预览 -->
  49. <div
  50. class="qrcode-preview"
  51. v-if="
  52. editForm.qrCodePosX &&
  53. editForm.qrCodePosY &&
  54. editForm.qrCodeWidth &&
  55. editForm.qrCodeHeight
  56. "
  57. :style="{
  58. left: editForm.qrCodePosX + 'px',
  59. top: editForm.qrCodePosY + 'px',
  60. width: editForm.qrCodeWidth + 'px',
  61. height: editForm.qrCodeHeight + 'px',
  62. background: '#e0e0e0',
  63. position: 'absolute',
  64. borderRadius: '50%'
  65. }"
  66. ></div>
  67. </div>
  68. </div>
  69. </div>
  70. <!-- 右侧编辑表单 -->
  71. <div class="edit-form">
  72. <el-form
  73. ref="formRef"
  74. :model="editForm"
  75. :rules="rules"
  76. label-width="100px"
  77. >
  78. <el-form-item label="模板名称" prop="name">
  79. <el-input
  80. v-model="editForm.name"
  81. placeholder="请输入模板名称"
  82. />
  83. </el-form-item>
  84. <el-form-item label="背景图片" prop="background">
  85. <image-upload
  86. v-model="editForm.background"
  87. :limit="1"
  88. />
  89. <div class="upload-tip">建议尺寸: 750 x 1334px</div>
  90. </el-form-item>
  91. <el-form-item label="微信头像位置">
  92. <el-row :gutter="10">
  93. <el-col :span="12">
  94. <span>X</span>
  95. <el-input-number
  96. v-model="editForm.headImgPosX"
  97. :min="0"
  98. :precision="0"
  99. :step="1"
  100. controls-position="right"
  101. placeholder="X"
  102. />
  103. </el-col>
  104. <el-col :span="12">
  105. <span>Y</span>
  106. <el-input-number
  107. v-model="editForm.headImgPosY"
  108. :min="0"
  109. :precision="0"
  110. :step="1"
  111. controls-position="right"
  112. placeholder="Y"
  113. />
  114. </el-col>
  115. </el-row>
  116. </el-form-item>
  117. <el-form-item label="微信名位置">
  118. <el-row :gutter="10">
  119. <el-col :span="12">
  120. <span>X</span>
  121. <el-input-number
  122. v-model="editForm.nickNamePosX"
  123. :min="0"
  124. :precision="0"
  125. :step="1"
  126. controls-position="right"
  127. placeholder="X"
  128. />
  129. </el-col>
  130. <el-col :span="12">
  131. <span>Y</span>
  132. <el-input-number
  133. v-model="editForm.nickNamePosY"
  134. :min="0"
  135. :precision="0"
  136. :step="1"
  137. controls-position="right"
  138. placeholder="Y"
  139. />
  140. </el-col>
  141. </el-row>
  142. </el-form-item>
  143. <el-form-item label="二维码位置">
  144. <el-row :gutter="10">
  145. <el-col :span="12">
  146. <span>X</span>
  147. <el-input-number
  148. v-model="editForm.qrCodePosX"
  149. :min="0"
  150. :precision="0"
  151. :step="1"
  152. controls-position="right"
  153. placeholder="X"
  154. />
  155. </el-col>
  156. <el-col :span="12">
  157. <span>Y</span>
  158. <el-input-number
  159. v-model="editForm.qrCodePosY"
  160. :min="0"
  161. :precision="0"
  162. :step="1"
  163. controls-position="right"
  164. placeholder="Y"
  165. />
  166. </el-col>
  167. </el-row>
  168. </el-form-item>
  169. <el-form-item label="二维码大小">
  170. <el-row :gutter="10">
  171. <el-col :span="12">
  172. <span>宽度</span>
  173. <el-input-number
  174. v-model="editForm.qrCodeWidth"
  175. :min="0"
  176. :precision="0"
  177. :step="1"
  178. controls-position="right"
  179. placeholder="宽度"
  180. />
  181. </el-col>
  182. <el-col :span="12">
  183. <span>高度</span>
  184. <el-input-number
  185. v-model="editForm.qrCodeHeight"
  186. :min="0"
  187. :precision="0"
  188. :step="1"
  189. controls-position="right"
  190. placeholder="高度"
  191. />
  192. </el-col>
  193. </el-row>
  194. </el-form-item>
  195. </el-form>
  196. </div>
  197. </div>
  198. <template #footer>
  199. <el-button @click="handleCancel">取消</el-button>
  200. <el-button type="primary" :loading="loading" @click="handleSave"
  201. >保存</el-button
  202. >
  203. </template>
  204. </el-dialog>
  205. </template>
  206. <script setup>
  207. import { ref, reactive } from 'vue';
  208. import { ElMessage } from 'element-plus';
  209. import ImageUpload from '@/components/ImageUpload/index.vue';
  210. import {
  211. getPosterTemplateInfo,
  212. addPosterTemplate,
  213. updatePosterTemplate
  214. } from '@/api/marketing/partner';
  215. const emit = defineEmits(['success']);
  216. // 表单实例
  217. const formRef = ref();
  218. // 编辑弹窗显示控制
  219. const visible = ref(false);
  220. // 加载状态
  221. const loading = ref(false);
  222. // 编辑表单数据
  223. const editForm = reactive({
  224. id: null,
  225. name: '',
  226. background: '',
  227. headImgPosX: '',
  228. headImgPosY: '',
  229. nickNamePosX: '',
  230. nickNamePosY: '',
  231. qrCodePosX: '',
  232. qrCodePosY: '',
  233. qrCodeWidth: '',
  234. qrCodeHeight: ''
  235. });
  236. // 表单验证规则
  237. const rules = {
  238. name: [{ required: true, message: '请输入模板名称', trigger: 'blur' }],
  239. background: [
  240. { required: true, message: '请上传背景图片', trigger: 'change' }
  241. ]
  242. };
  243. // 打开弹窗
  244. const open = (row) => {
  245. visible.value = true;
  246. if (row?.id) {
  247. getDetail(row.id);
  248. } else {
  249. resetForm();
  250. }
  251. };
  252. // 获取详情
  253. const getDetail = async (id) => {
  254. try {
  255. const res = await getPosterTemplateInfo(id);
  256. if (res.data.code == 200) {
  257. Object.assign(editForm, res.data.data);
  258. }
  259. } catch (error) {
  260. ElMessage.error(error.message);
  261. }
  262. };
  263. // 重置表单
  264. const resetForm = () => {
  265. formRef.value?.resetFields();
  266. Object.assign(editForm, {
  267. id: null,
  268. name: '',
  269. background: '',
  270. headImgPosX: 0,
  271. headImgPosY: 0,
  272. nickNamePosX: 0,
  273. nickNamePosY: 0,
  274. qrCodePosX: 0,
  275. qrCodePosY: 0,
  276. qrCodeWidth: 0,
  277. qrCodeHeight: 0
  278. });
  279. };
  280. // 取消
  281. const handleCancel = () => {
  282. visible.value = false;
  283. };
  284. // 保存海报
  285. const handleSave = () => {
  286. formRef.value?.validate(async (valid) => {
  287. if (!valid) return;
  288. try {
  289. loading.value = true;
  290. const saveOrUpdate = editForm.id
  291. ? updatePosterTemplate
  292. : addPosterTemplate;
  293. await saveOrUpdate(editForm);
  294. ElMessage.success('保存成功');
  295. visible.value = false;
  296. emit('success');
  297. } catch (error) {
  298. ElMessage.error(error.message);
  299. } finally {
  300. loading.value = false;
  301. }
  302. });
  303. };
  304. defineExpose({ open });
  305. </script>
  306. <style lang="scss" scoped>
  307. .poster-edit-container {
  308. display: flex;
  309. gap: 20px;
  310. .mobile-preview {
  311. background: url('@/assets/mobile.png') no-repeat center center;
  312. background-size: 100% 100%;
  313. width: 445px;
  314. height: 750px;
  315. border-radius: 20px;
  316. overflow: hidden;
  317. flex-shrink: 0;
  318. .preview-content {
  319. padding: 16px;
  320. .preview-poster {
  321. position: relative;
  322. width: 250px;
  323. height: 445px;
  324. margin: 110px 82px;
  325. overflow: hidden;
  326. }
  327. }
  328. }
  329. .edit-form {
  330. flex: 1;
  331. padding: 20px;
  332. background: #fff;
  333. border-radius: 4px;
  334. }
  335. .el-col-12 {
  336. display: flex;
  337. align-items: center;
  338. justify-content: center;
  339. span {
  340. min-width: 40px;
  341. text-align: center;
  342. margin-right: 10px;
  343. }
  344. }
  345. }
  346. .upload-tip {
  347. font-size: 12px;
  348. color: #999;
  349. margin-top: 4px;
  350. }
  351. </style>