cardPoster.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <script setup lang="ts">
  2. import html2canvas from 'html2canvas'
  3. const simplicityList = import.meta.glob('./images/Simplicity/*.png')
  4. const affairsList = import.meta.glob('./images/affairs/*.png')
  5. interface Questionnaire {
  6. title: string
  7. desc: string
  8. }
  9. interface defaultData {
  10. //海报二维码
  11. qrCode: string
  12. //绑定
  13. modelValue: boolean
  14. //公司名称
  15. cardTitle: string
  16. //内容与描述
  17. questionnaire?: Questionnaire
  18. }
  19. const props = withDefaults(defineProps<defaultData>(), {
  20. qrCode: '',
  21. cardTitle: ''
  22. })
  23. const questionnaire = ref<Questionnaire>({
  24. title: '',
  25. desc: ''
  26. })
  27. function initializeQuestionnaire(qr: Questionnaire | undefined): Questionnaire {
  28. if (qr) {
  29. const copiedQr = JSON.parse(JSON.stringify(qr))
  30. return {
  31. title: copiedQr.title,
  32. desc: copiedQr.desc
  33. }
  34. } else {
  35. return {
  36. title: '',
  37. desc: ''
  38. }
  39. }
  40. }
  41. questionnaire.value = initializeQuestionnaire(props.questionnaire)
  42. const Simplicity = ref<any>(Object.keys(simplicityList).map(key => new URL(key, import.meta.url).href))
  43. const affairs = ref<any>(Object.keys(affairsList).map(key => new URL(key, import.meta.url).href))
  44. const WHITE_COLOR = '#FFFFFF'
  45. const BLACK_COLOR = '#000000'
  46. const emits = defineEmits(['update:modelValue'])
  47. const templateType = computed({
  48. get: () => props.modelValue,
  49. set: value => {
  50. emits('update:modelValue', value)
  51. }
  52. })
  53. const posterRef = ref<any>(null)
  54. const activeName = ref<Number>(1)
  55. const questionBg = ref<any>(Simplicity.value[0])
  56. const radio1 = ref<String>('居中')
  57. const color1 = ref(WHITE_COLOR)
  58. const color2 = ref(BLACK_COLOR)
  59. const saveimg = (item: string) => {
  60. activeName.value = 1
  61. questionBg.value = item
  62. }
  63. const submitForm = () => {
  64. html2canvas(posterRef.value).then(canvas => {
  65. let baseImg = canvas.toDataURL('image/png')
  66. let save = document.createElement('a')
  67. save.href = baseImg
  68. save.download = '分享海报'
  69. save.click()
  70. })
  71. }
  72. const resetForm = () => {
  73. templateType.value = false
  74. }
  75. const radioChange = (e: any) => {
  76. color1.value = e === '居中' ? WHITE_COLOR : BLACK_COLOR
  77. color2.value = color1.value
  78. }
  79. </script>
  80. <template>
  81. <el-dialog v-model="templateType" width="800px" title="分享海报">
  82. <el-tabs v-model="activeName" class="demo-tabs" style="border-bottom: 1px solid #e5e5e5">
  83. <el-tab-pane label="海报模版" :name="0">
  84. <div class="text-lg font-bold mb-4">简约</div>
  85. <div class="flex justify-between">
  86. <div class="relative template_img" v-for="(item, index) in Simplicity" :key="index">
  87. <img class="template_img" :src="item" />
  88. <div class="absolute add_img">
  89. <div class="template_img flex items-center justify-center">
  90. <el-button type="primary" @click="saveimg(item)">使用模版</el-button>
  91. </div>
  92. </div>
  93. </div>
  94. </div>
  95. <div class="text-lg font-bold mb-4 mt-4">商务</div>
  96. <div class="flex justify-between">
  97. <div class="relative template_img" v-for="(item, index) in affairs" :key="index">
  98. <img class="template_img" :src="item" />
  99. <div class="absolute add_img">
  100. <div class="template_img flex items-center justify-center">
  101. <el-button type="primary" @click="saveimg(item)">使用模版</el-button>
  102. </div>
  103. </div>
  104. </div>
  105. </div>
  106. </el-tab-pane>
  107. <el-tab-pane label="生成海报" :name="1">
  108. <div class="flex">
  109. <div ref="posterRef" class="flex items-center justify-center new_box">
  110. <div class="relative">
  111. <img :src="questionBg" class="relative_img" />
  112. <div v-if="radio1 == '底部'" class="absolute flex justify-center items-center top-3 right-3">
  113. <img class="w-20px h-20px mr-5px" src="/logo.png" alt="qrcode" />
  114. <div class="font-bold" style="color: white">{{ props.cardTitle }}</div>
  115. </div>
  116. <div v-if="radio1 == '底部'" class="absolute p-3 flex items-center bottom-0 w-full relative_bg">
  117. <img class="mr-3 relative_btm_img" :src="props.qrCode" />
  118. <div>
  119. <div class="text-s font-bold" :style="{ color: color1 }">{{ questionnaire.title }}</div>
  120. <div class="text-xs font-bold" :style="{ color: color2 }">{{ questionnaire.desc }}</div>
  121. </div>
  122. </div>
  123. <div v-else class="absolute p-3 top-0 w-full">
  124. <div class="text-xl font-bold text-center mb-7" :style="{ color: color1 }">
  125. {{ questionnaire.title }}
  126. </div>
  127. <div class="text-center mb-6">
  128. <img class="relative_cen_img" :src="props.qrCode" />
  129. </div>
  130. <div class="mb-3">
  131. <div class="text-s font-bold text-center" :style="{ color: color2 }">
  132. {{ questionnaire.desc }}
  133. </div>
  134. </div>
  135. <div class="flex justify-center items-center">
  136. <img class="w-20px h-20px mr-5px" src="/logo.png" alt="qrcode" />
  137. <div class="font-bold" style="color: white">{{ props.cardTitle }}</div>
  138. </div>
  139. </div>
  140. </div>
  141. </div>
  142. <div class="separate"></div>
  143. <div class="new_box">
  144. <div class="flex justify-between">
  145. <div class="text-text-base font-bold mb-4">海报标题</div>
  146. <div><el-color-picker v-model="color1" />{{ color1 }}</div>
  147. </div>
  148. <div class="mb-4 w-full">
  149. <el-input v-model="questionnaire.title" maxlength="20" show-word-limit class="w-full"></el-input>
  150. </div>
  151. <div class="flex justify-between">
  152. <div class="text-text-base font-bold mb-4">海报文本</div>
  153. <div><el-color-picker v-model="color2" />{{ color2 }}</div>
  154. </div>
  155. <div class="mb-4">
  156. <el-input
  157. v-model="questionnaire.desc"
  158. type="textarea"
  159. show-word-limit
  160. maxlength="80"
  161. :rows="4"
  162. ></el-input>
  163. </div>
  164. <div>
  165. <div class="text-text-base font-bold mb-4">二维码位置</div>
  166. </div>
  167. <div>
  168. <el-radio-group v-model="radio1" size="large" @change="radioChange">
  169. <el-radio-button label="居中" />
  170. <el-radio-button label="底部" />
  171. </el-radio-group>
  172. </div>
  173. </div>
  174. </div>
  175. </el-tab-pane>
  176. </el-tabs>
  177. <template #footer>
  178. <div class="flex justify-end w-full">
  179. <el-button size="default" @click="resetForm()">取消</el-button>
  180. <el-button size="default" type="primary" @click="submitForm()">保存下载</el-button>
  181. </div>
  182. </template>
  183. </el-dialog>
  184. </template>
  185. <style lang="scss" scoped>
  186. .template_img {
  187. width: 130px;
  188. height: 174px;
  189. border-radius: 10px;
  190. }
  191. .template_img:hover .add_img {
  192. display: block;
  193. }
  194. .add_img {
  195. background-color: #ffffff99;
  196. z-index: 999;
  197. top: 0px;
  198. display: none;
  199. }
  200. .separate {
  201. background-color: #e5e5e5;
  202. width: 1px;
  203. height: 454px;
  204. margin: 0 30px;
  205. }
  206. .new_box {
  207. width: 370px;
  208. height: 454px;
  209. }
  210. .relative_img {
  211. width: 310px;
  212. height: 406px;
  213. border-radius: 10px;
  214. }
  215. .relative_bg {
  216. background-color: #ffffff99;
  217. }
  218. .relative_btm_img {
  219. width: 80px;
  220. height: 80px;
  221. border-radius: 5px;
  222. }
  223. .relative_cen_img {
  224. width: 150px;
  225. height: 150px;
  226. border-radius: 5px;
  227. }
  228. </style>