Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cab6d0b0c | ||
|
|
a1de189afc | ||
|
|
6395633719 | ||
|
|
c91989e6ed | ||
|
|
ee12fbcea3 | ||
|
|
f95ac4a6d8 | ||
|
|
0eaf087177 | ||
|
|
c8437b28af |
@@ -6,6 +6,20 @@
|
||||
> 作为一款私域引流工具,凭借其强大的功能和灵活的应用场景,成为了推广营销领域的热门选择,它支持从抖音直接跳转到个人微信、微信群、小程序以及企业微信客服,有效打通了不同平台之间的壁垒,实现了流量的高效转化和利用,推广营销必备!
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 系统演示
|
||||
```
|
||||
演示地址:https://open.mayilink.cn/
|
||||
管理员账号:admin
|
||||
管理员密码:admin123
|
||||
用户账号:13999999999
|
||||
用户密码:123456
|
||||
```
|
||||
|
||||
|
||||
### 目录结构
|
||||
```
|
||||
- admin 后台管理
|
||||
@@ -76,7 +90,15 @@ let baseUrl = "https://xxx.xxxxx.xxx"; // 后台请求域名改为你的域名
|
||||
|
||||
|
||||
## 重要信息
|
||||
|
||||
欢迎进群获取最新开发计划或交流技术
|
||||
|
||||
如需进群请添加微信号
|
||||
|
||||
微信号:tok4mi
|
||||
|
||||
添加请备注:交流群
|
||||
|
||||
项目合作洽谈,请联系客服微信(使用微信扫码添加好友,请注明来意)。
|
||||
|
||||

|
||||
|
||||
<img src="https://huoxing.tos-cn-shanghai.volces.com/2024/11/05/HFiFy0AaqSLxmlFb0RScBKgLtGVzC9xXb7M9HCJ5.jpg" width="400">
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"qs": "^6.11.2",
|
||||
"sortablejs": "^1.15.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-cropper": "^1.1.4",
|
||||
"vue-router": "^4.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Generated
+19
-4
@@ -59,6 +59,9 @@ importers:
|
||||
vue:
|
||||
specifier: ^3.3.4
|
||||
version: 3.3.4
|
||||
vue-cropper:
|
||||
specifier: ^1.1.4
|
||||
version: 1.1.4
|
||||
vue-router:
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2(vue@3.3.4)
|
||||
@@ -322,6 +325,7 @@ packages:
|
||||
'@humanwhocodes/config-array@0.11.10':
|
||||
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
deprecated: Use @eslint/config-array instead
|
||||
|
||||
'@humanwhocodes/module-importer@1.0.1':
|
||||
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
||||
@@ -329,6 +333,7 @@ packages:
|
||||
|
||||
'@humanwhocodes/object-schema@1.2.1':
|
||||
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||
deprecated: Use @eslint/object-schema instead
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.4.15':
|
||||
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
|
||||
@@ -495,6 +500,7 @@ packages:
|
||||
|
||||
'@volar/vue-typescript@1.6.5':
|
||||
resolution: {integrity: sha512-er9rVClS4PHztMUmtPMDTl+7c7JyrxweKSAEe/o/Noeq2bQx6v3/jZHVHBe8ZNUti5ubJL/+Tg8L3bzmlalV8A==}
|
||||
deprecated: 'WARNING: This project has been renamed to @vue/typescript. Install using @vue/typescript instead.'
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
|
||||
@@ -877,8 +883,8 @@ packages:
|
||||
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
cross-spawn@6.0.5:
|
||||
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
|
||||
cross-spawn@6.0.6:
|
||||
resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==}
|
||||
engines: {node: '>=4.8'}
|
||||
|
||||
cross-spawn@7.0.3:
|
||||
@@ -1134,6 +1140,7 @@ packages:
|
||||
eslint@8.39.0:
|
||||
resolution: {integrity: sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
|
||||
hasBin: true
|
||||
|
||||
espree@9.6.1:
|
||||
@@ -1313,6 +1320,7 @@ packages:
|
||||
|
||||
glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
globals@13.20.0:
|
||||
resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
|
||||
@@ -1448,6 +1456,7 @@ packages:
|
||||
|
||||
inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
@@ -2109,6 +2118,7 @@ packages:
|
||||
|
||||
rimraf@3.0.2:
|
||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
|
||||
rollup@3.26.3:
|
||||
@@ -2507,6 +2517,9 @@ packages:
|
||||
terser:
|
||||
optional: true
|
||||
|
||||
vue-cropper@1.1.4:
|
||||
resolution: {integrity: sha512-5m98vBsCEI9rbS4JxELxXidtAui3qNyTHLHg67Qbn7g8cg+E6LcnC+hh3SM/p94x6mFh6KRxT1ttnta+wCYqWA==}
|
||||
|
||||
vue-demi@0.14.5:
|
||||
resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -3409,7 +3422,7 @@ snapshots:
|
||||
object-assign: 4.1.1
|
||||
vary: 1.1.2
|
||||
|
||||
cross-spawn@6.0.5:
|
||||
cross-spawn@6.0.6:
|
||||
dependencies:
|
||||
nice-try: 1.0.5
|
||||
path-key: 2.0.1
|
||||
@@ -4553,7 +4566,7 @@ snapshots:
|
||||
dependencies:
|
||||
ansi-styles: 3.2.1
|
||||
chalk: 2.4.2
|
||||
cross-spawn: 6.0.5
|
||||
cross-spawn: 6.0.6
|
||||
memorystream: 0.3.1
|
||||
minimatch: 3.1.2
|
||||
pidtree: 0.3.1
|
||||
@@ -5281,6 +5294,8 @@ snapshots:
|
||||
fsevents: 2.3.2
|
||||
sass: 1.64.0
|
||||
|
||||
vue-cropper@1.1.4: {}
|
||||
|
||||
vue-demi@0.14.5(vue@3.3.4):
|
||||
dependencies:
|
||||
vue: 3.3.4
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"VITE_API_URL": "https://xxxx.xxxxxx.com/api"
|
||||
}
|
||||
@@ -48,3 +48,6 @@ export const ApiConsumeCard = (secret: string) => {
|
||||
export const ApiUserFix = (id: any, data: any) => {
|
||||
return http.put(`/users/${id}`, data)
|
||||
}
|
||||
export const ApiUploadBase64 = (data: any) => {
|
||||
return http.post<any, any>('/material-base64', data)
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 784 KiB After Width: | Height: | Size: 776 KiB |
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<el-button type="primary" @click="dialogVisible = true">上传图片</el-button>
|
||||
<el-dialog
|
||||
title="上传图片"
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
destroy-on-close
|
||||
width="600px"
|
||||
>
|
||||
<div class="avatar-container">
|
||||
<!-- 待上传图片 -->
|
||||
|
||||
<div v-show="!options.img">
|
||||
<el-upload
|
||||
class="upload"
|
||||
ref="tailorUpload"
|
||||
action="#"
|
||||
:on-change="upload"
|
||||
:show-file-list="false"
|
||||
:auto-upload="false"
|
||||
>
|
||||
<el-button slot="trigger" type="primary" ref="uploadBtn"> 选择图片 </el-button>
|
||||
</el-upload>
|
||||
<div>支持jpg、png格式的图片,大小不超过5M</div>
|
||||
</div>
|
||||
|
||||
<div v-show="options.img" class="avatar-crop">
|
||||
<vueCropper
|
||||
class="crop-box"
|
||||
ref="cropper"
|
||||
:img="options.img"
|
||||
:autoCrop="options.autoCrop"
|
||||
:fixedBox="options.fixedBox"
|
||||
:canMoveBox="options.canMoveBox"
|
||||
:autoCropWidth="options.autoCropWidth"
|
||||
:autoCropHeight="options.autoCropHeight"
|
||||
:centerBox="options.centerBox"
|
||||
:fixed="options.fixed"
|
||||
:fixedNumber="options.fixedNumber"
|
||||
:canMove="options.canMove"
|
||||
:canScale="options.canScale"
|
||||
:full="options.full"
|
||||
:outputSize="0.1"
|
||||
outputType="png"
|
||||
></vueCropper>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<div class="reupload" @click="reupload">
|
||||
<span v-show="options.img">重新上传</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="closeDialog" :loading="loading">取 消</el-button>
|
||||
<el-button type="primary" @click="getCrop" :loading="loading">确 定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VueCropper } from 'vue-cropper'
|
||||
import { ref, reactive } from 'vue'
|
||||
import 'vue-cropper/dist/index.css'
|
||||
import { ElMessage, type UploadProps } from 'element-plus'
|
||||
import { ApiUploadBase64 } from '@/api/user'
|
||||
|
||||
const cropper = ref()
|
||||
const uploadBtn = ref()
|
||||
const tailorUpload = ref()
|
||||
const dialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const options = reactive({
|
||||
img: '', // 原图文件
|
||||
autoCrop: true, // 默认生成截图框
|
||||
fixedBox: false, // 固定截图框大小
|
||||
canMoveBox: true, // 截图框可以拖动
|
||||
autoCropWidth: 200, // 截图框宽度
|
||||
autoCropHeight: 200, // 截图框高度
|
||||
fixed: true, // 截图框宽高固定比例
|
||||
fixedNumber: [1, 1], // 截图框的宽高比例
|
||||
centerBox: true, // 截图框被限制在图片里面
|
||||
canMove: true, // 上传图片不允许拖动
|
||||
canScale: true, // 上传图片不允许滚轮缩放
|
||||
full: false //是否输出原图比例的截图
|
||||
} as any)
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const props = defineProps<{
|
||||
modelValue?: string | string[]
|
||||
}>()
|
||||
const upload: UploadProps['onChange'] = (file: any) => {
|
||||
const isIMAGE = file.raw.type === 'image/jpeg' || file.raw.type === 'image/png'
|
||||
const isLt5M = file.raw.size / 1024 / 1024 < 5
|
||||
if (!isIMAGE) {
|
||||
ElMessage.warning('请选择 jpg、png 格式的图片')
|
||||
return false
|
||||
}
|
||||
if (!isLt5M) {
|
||||
ElMessage.warning('图片大小不能超过 5MB')
|
||||
return false
|
||||
}
|
||||
let reader = new FileReader()
|
||||
|
||||
reader.readAsDataURL(file.raw)
|
||||
//console.log(reader);
|
||||
|
||||
reader.onload = (e: any) => {
|
||||
options.img = e.target.result // base64
|
||||
}
|
||||
tailorUpload.value.clearFiles() //这里处理重新上传时,upload组件change事件错误问题
|
||||
}
|
||||
// 关闭弹框
|
||||
const closeDialog = () => {
|
||||
options.img = ''
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const dealImage = (base64: any, w: any, callback: any) => {
|
||||
const newImage: any = new Image()
|
||||
let quality = 0.8 //压缩系数0-1之间
|
||||
newImage.src = base64
|
||||
newImage.setAttribute('crossOrigin', 'Anonymous') //url为外域时需要
|
||||
let imgWidth, imgHeight
|
||||
newImage.onload = function () {
|
||||
imgWidth = newImage.width
|
||||
imgHeight = newImage.height
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx: any = canvas.getContext('2d')
|
||||
if (Math.max(imgWidth, imgHeight) > w) {
|
||||
if (imgWidth > imgHeight) {
|
||||
canvas.width = w
|
||||
canvas.height = (w * imgHeight) / imgWidth
|
||||
} else {
|
||||
canvas.height = w
|
||||
canvas.width = (w * imgWidth) / imgHeight
|
||||
}
|
||||
} else {
|
||||
canvas.width = imgWidth
|
||||
canvas.height = imgHeight
|
||||
quality = 0.8
|
||||
}
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
|
||||
let base64 = canvas.toDataURL('image/jpeg', quality) //压缩语句
|
||||
// 如想确保图片压缩到自己想要的尺寸,如要求在50-100kb之间,请加以下语句,quality初始值根据情况自定
|
||||
// while (base64.length / 1024 > 100) {
|
||||
// quality -= 0.01;
|
||||
// base64 = canvas.toDataURL("image/jpeg", quality);
|
||||
// }
|
||||
// // 防止最后一次压缩低于最低尺寸,只要quality递减合理,无需考虑
|
||||
// while (base64.length / 1024 < 10) {
|
||||
// quality += 0.001;
|
||||
// base64 = canvas.toDataURL("image/jpeg", quality);
|
||||
// }
|
||||
callback(base64 as any) //必须通过回调函数返回,否则无法及时拿到该值
|
||||
}
|
||||
}
|
||||
|
||||
// 获取截图信息
|
||||
const getCrop = () => {
|
||||
// 获取截图的 base64 数据
|
||||
|
||||
cropper.value.getCropData((data: any) => {
|
||||
dealImage(data, 300, (res: any) => {
|
||||
ApiUploadBase64({ image: res })
|
||||
.then((resData) => {
|
||||
loading.value = false
|
||||
emits('update:modelValue', resData.path || '')
|
||||
ElMessage.success('上传成功')
|
||||
tailorUpload.value.clearFiles()
|
||||
closeDialog()
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
// 重新上传
|
||||
const reupload = () => {
|
||||
uploadBtn.value.ref.click()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
.reupload {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.avatar-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
background-color: #f0f2f5;
|
||||
margin-right: 10px;
|
||||
border-radius: 4px;
|
||||
.upload {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.avatar-crop {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
position: relative;
|
||||
.crop-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -21,7 +21,12 @@ export const ImagePath = (images: any) => {
|
||||
|
||||
let newPath: any
|
||||
if (configStore.asset_url) {
|
||||
newPath = configStore.asset_url + path
|
||||
// asset_url 或者path第一位是否带有斜杠
|
||||
if (path.indexOf('/') === 0 && configStore.asset_url.replace(/\/$/, '').length > 0) {
|
||||
newPath = configStore.asset_url.slice(0, -1) + path
|
||||
} else {
|
||||
newPath = configStore.asset_url + path
|
||||
}
|
||||
}
|
||||
return newPath
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<div class="sidebar-logo sidebar-logo-expend">
|
||||
<transition enter-active-class="animate__animated animate__fadeInLeft">
|
||||
<div class="logo_icon" v-if="appStore.sidebarOpened">
|
||||
<img class="logo_icon_item" v-if="!userStore.userInfo.logo" src="@/assets/menu/logo.png" alt="" />
|
||||
<img class="logo_icon_item" v-else :src="ImagePath(userStore.userInfo.logo)" alt="" />
|
||||
<span class="logo-title">{{ userStore.userInfo.site_name || '火星快链' }}</span>
|
||||
</div>
|
||||
<div v-else class="logo_icon">
|
||||
<img class="logo_icon_item" v-if="!userStore.userInfo.logo" src="@/assets/menu/logo.png" alt="" />
|
||||
<img class="logo_icon_item" v-else :src="ImagePath(userStore.userInfo.logo)" alt="" />
|
||||
<div class="logo_icon">
|
||||
<img class="logo_icon_item" v-if="!configStore.web_site_logo" src="@/assets/menu/logo.png" alt="" />
|
||||
<img class="logo_icon_item" v-else :src="logPath" alt="" />
|
||||
<span class="logo-title">{{ configStore.web_site_title || '火星快链' }}</span>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { appStore, userStore } from '@/stores'
|
||||
import { configStore } from '@/stores'
|
||||
import { ImagePath } from '@/hooks/image'
|
||||
import { computed } from 'vue'
|
||||
const logPath = computed(() => {
|
||||
return ImagePath(configStore.web_site_logo)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -14,7 +14,7 @@ interface WX {
|
||||
title: string
|
||||
sub_title: string
|
||||
qr: QR[]
|
||||
switch_type: number
|
||||
switch_type?: number
|
||||
uv_limit_type: number
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,33 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ApiGetSet } from '@/api/comment'
|
||||
|
||||
const useConfigStore = defineStore('init', {
|
||||
state: () => ({
|
||||
code_mode: 0,
|
||||
package: [] as Array<any>,
|
||||
asset_url: ''
|
||||
asset_url: '',
|
||||
web_site_bottom_logo: '',
|
||||
web_site_customer_service: '',
|
||||
web_site_logo: '',
|
||||
web_site_title: ''
|
||||
}),
|
||||
actions: {
|
||||
refresh(res: any) {
|
||||
this.code_mode = res.code_mode
|
||||
this.package = res.package
|
||||
this.asset_url = res.asset_url
|
||||
this.web_site_bottom_logo = res.web_site_bottom_logo
|
||||
this.web_site_customer_service = res.web_site_customer_service
|
||||
this.web_site_logo = res.web_site_logo
|
||||
this.web_site_title = res.web_site_title
|
||||
},
|
||||
// 刷新配置
|
||||
refreshConfig() {
|
||||
ApiGetSet().then(res=>{
|
||||
this.refresh(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
export default useConfigStore
|
||||
|
||||
@@ -10,12 +10,10 @@ import { stringify } from 'qs'
|
||||
import router from '@/router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { userStore } from '@/stores'
|
||||
import { VITE_API_URL } from '../../public/config.json'
|
||||
|
||||
const http: AxiosInstance = axios.create({
|
||||
baseURL:
|
||||
import.meta.env.MODE === 'production'
|
||||
? `${window.location.origin}/api`
|
||||
: import.meta.env.VITE_PROXY_PATH,
|
||||
baseURL: import.meta.env.MODE === 'production' ? VITE_API_URL : import.meta.env.VITE_PROXY_PATH,
|
||||
timeout: 60000,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
||||
@@ -59,9 +59,24 @@ onMounted(() => {
|
||||
router.push('/home')
|
||||
}
|
||||
})
|
||||
const getImageUrl = (url: string, path: string) => {
|
||||
// 判断url最后一位和路径的第一位是否有斜杠
|
||||
if (url.substr(-1) != '/' && path.substr(0, 1) != '/') {
|
||||
url = url + '/'
|
||||
}
|
||||
// 如果url最后一位有斜杠 或者 路径最后一位有斜杠就去掉一个
|
||||
if (url.substr(-1) == '/' && path.substr(0, 1) == '/') {
|
||||
url = url.substr(0, url.length - 1)
|
||||
}
|
||||
return url + path
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="login-warp">
|
||||
<div class="login-warp relative">
|
||||
<div class="logo flex align-center">
|
||||
<img class="img" :src="getImageUrl(configStore.asset_url, configStore.web_site_logo)" />
|
||||
<div class="title">{{ configStore.web_site_title }}</div>
|
||||
</div>
|
||||
<div class="login-right">
|
||||
<!-- 登录页 -->
|
||||
<div class="login-main">
|
||||
@@ -70,11 +85,26 @@ onMounted(() => {
|
||||
<el-icon><Back /></el-icon>
|
||||
</div>
|
||||
<h2 style="color: #000">欢迎登录</h2>
|
||||
<el-form ref="loginFormRef" :disabled="loading.status" :model="loginData" @keyup.enter="submit(loginFormRef)">
|
||||
<el-form-item prop="username" :rules="[{ required: true, message: '请输入账号', trigger: 'blur' }]">
|
||||
<el-input v-model="loginData.username" placeholder="请输入账号" prefix-icon="el-icon-user" />
|
||||
<el-form
|
||||
ref="loginFormRef"
|
||||
:disabled="loading.status"
|
||||
:model="loginData"
|
||||
@keyup.enter="submit(loginFormRef)"
|
||||
>
|
||||
<el-form-item
|
||||
prop="username"
|
||||
:rules="[{ required: true, message: '请输入账号', trigger: 'blur' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="loginData.username"
|
||||
placeholder="请输入账号"
|
||||
prefix-icon="el-icon-user"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
|
||||
<el-form-item
|
||||
prop="password"
|
||||
:rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]"
|
||||
>
|
||||
<el-input
|
||||
show-password
|
||||
v-model="loginData.password"
|
||||
@@ -83,8 +113,12 @@ onMounted(() => {
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="login-btn" type="primary" @click="submit(loginFormRef)" :loading="loading.status"
|
||||
>立即登录</el-button
|
||||
<el-button
|
||||
class="login-btn"
|
||||
type="primary"
|
||||
@click="submit(loginFormRef)"
|
||||
:loading="loading.status"
|
||||
>立即登录</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -110,6 +144,24 @@ onMounted(() => {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
background-image: url('@/assets/login/login-bg.png');
|
||||
.logo {
|
||||
position: absolute;
|
||||
z-index: 7;
|
||||
top: 40px;
|
||||
left: 40px;
|
||||
.img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-left: 20px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.login-right {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -4,8 +4,6 @@ import FormModal from '@/components/FormModal.vue'
|
||||
import CreateOrLook from '@/views/link/addLink/components/app/create-or-look.vue'
|
||||
import OssImage from '@/components/OssImage.vue'
|
||||
import { copyLink } from '@/utils'
|
||||
import { ApiRenewalLink } from '@/api/comment'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
|
||||
const user = useUserStore()
|
||||
@@ -20,24 +18,7 @@ const { state, sizeChangeHandle, currentChangeHandle, showForm, submitForm, rese
|
||||
username: ''
|
||||
}
|
||||
})
|
||||
// 续费
|
||||
const renewal = (id: number) => {
|
||||
ElMessageBox.confirm('确定要续费一个月吗?这将要扣除1个创建个数', 'Warning', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
center: true
|
||||
})
|
||||
.then(() => {
|
||||
ApiRenewalLink({
|
||||
id: id
|
||||
}).then(() => {
|
||||
ElMessage.success('操作成功!')
|
||||
research()
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -74,9 +55,9 @@ const renewal = (id: number) => {
|
||||
:row-key="state.primaryKey"
|
||||
>
|
||||
<el-table-column prop="id" label="序号"></el-table-column>
|
||||
<el-table-column label="图标">
|
||||
<el-table-column label="图片" >
|
||||
<template #default="{ row }">
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60"></oss-image>
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60" style="margin: 0 auto"></oss-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="卡片标题"></el-table-column>
|
||||
|
||||
@@ -18,24 +18,7 @@ const { state, sizeChangeHandle, currentChangeHandle, showForm, submitForm, rese
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
// 续费
|
||||
const renewal = (id: number) => {
|
||||
ElMessageBox.confirm('确定要续费一个月吗?这将要扣除1个创建个数', 'Warning', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
center: true
|
||||
})
|
||||
.then(() => {
|
||||
ApiRenewalLink({
|
||||
id: id
|
||||
}).then(() => {
|
||||
ElMessage.success('操作成功!')
|
||||
research()
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
const user = useUserStore()
|
||||
</script>
|
||||
|
||||
@@ -73,9 +56,9 @@ const user = useUserStore()
|
||||
:row-key="state.primaryKey"
|
||||
>
|
||||
<el-table-column prop="id" label="序号"></el-table-column>
|
||||
<el-table-column label="图标">
|
||||
<el-table-column label="图片">
|
||||
<template #default="{ row }">
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60"></oss-image>
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60" style="margin: 0 auto"></oss-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="卡片标题"></el-table-column>
|
||||
|
||||
@@ -18,24 +18,7 @@ const { state, sizeChangeHandle, currentChangeHandle, showForm, submitForm, rese
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
// 续费
|
||||
const renewal = (id: number) => {
|
||||
ElMessageBox.confirm('确定要续费一个月吗?这将要扣除1个创建个数', 'Warning', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
center: true
|
||||
})
|
||||
.then(() => {
|
||||
ApiRenewalLink({
|
||||
id: id
|
||||
}).then(() => {
|
||||
ElMessage.success('操作成功!')
|
||||
research()
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
const user = useUserStore()
|
||||
</script>
|
||||
|
||||
@@ -73,9 +56,9 @@ const user = useUserStore()
|
||||
:row-key="state.primaryKey"
|
||||
>
|
||||
<el-table-column prop="id" label="序号"></el-table-column>
|
||||
<el-table-column label="图标">
|
||||
<el-table-column label="图片">
|
||||
<template #default="{ row }">
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60"></oss-image>
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60" style="margin: 0 auto"></oss-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="卡片标题"></el-table-column>
|
||||
|
||||
@@ -18,24 +18,7 @@ const { state, sizeChangeHandle, currentChangeHandle, showForm, submitForm, rese
|
||||
page: 1
|
||||
}
|
||||
})
|
||||
// 续费
|
||||
const renewal = (id: number) => {
|
||||
ElMessageBox.confirm('确定要续费一个月吗?这将要扣除1个创建个数', 'Warning', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
center: true
|
||||
})
|
||||
.then(() => {
|
||||
ApiRenewalLink({
|
||||
id: id
|
||||
}).then(() => {
|
||||
ElMessage.success('操作成功!')
|
||||
research()
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
const user = useUserStore()
|
||||
</script>
|
||||
|
||||
@@ -72,9 +55,9 @@ const user = useUserStore()
|
||||
:row-key="state.primaryKey"
|
||||
>
|
||||
<el-table-column prop="id" label="序号"></el-table-column>
|
||||
<el-table-column label="图标">
|
||||
<el-table-column label="图片">
|
||||
<template #default="{ row }">
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60"></oss-image>
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60" style="margin: 0 auto"></oss-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="卡片标题"></el-table-column>
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<script setup lang="ts">
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
import MaterialSelect from '@/components/MaterialSelect/MaterialSelect.vue'
|
||||
import useForm from '@/hooks/form'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { ApiDomainEnableList } from '@/api/domain'
|
||||
import { ApiAppList } from '@/api/app'
|
||||
import type { linkConfig } from '@/models/link'
|
||||
import OssImage from '@/components/OssImage.vue'
|
||||
import UploadImage from '@/components/uploadImage.vue'
|
||||
import QrLink from '@/views/link/addLink/components/qr/qrLink.vue'
|
||||
import { configStore, userStore } from '@/stores'
|
||||
import { getNextMonth } from '@/utils/dateTime'
|
||||
|
||||
const formRef = ref()
|
||||
const { formData, formLoading } = useForm({
|
||||
formRef,
|
||||
defVal(detail) {
|
||||
return {
|
||||
title: detail?.title, // 标题
|
||||
type: '11', // 类型
|
||||
description: detail?.description, // 描述
|
||||
icon: detail?.icon, // 封面图
|
||||
remark: detail?.remark, // 备注
|
||||
config: detail?.config ? detail?.config : ({} as linkConfig) // 无风险域名 / 小程序 / 地址
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 获取域名列表
|
||||
const domainList = ref()
|
||||
const getDomainList = async () => {
|
||||
domainList.value = await ApiDomainEnableList()
|
||||
}
|
||||
|
||||
// 获取小程序列表
|
||||
const miniProgramList = ref()
|
||||
const getMiniProgramList = async () => {
|
||||
miniProgramList.value = await ApiAppList()
|
||||
}
|
||||
|
||||
const rules = {
|
||||
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
|
||||
'config.domain_id': [{ required: true, message: '请选择域名', trigger: 'blur' }],
|
||||
// 'config.min_id': [{ required: true, message: '请选择小程序', trigger: 'blur' }],
|
||||
'config.url': [{ required: true, message: '请选择二维码图片', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const userInfo = userStore
|
||||
onMounted(() => {
|
||||
getDomainList()
|
||||
getMiniProgramList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="margin-top: -30px" class="m-b-20">
|
||||
<el-space class="text-main">
|
||||
<span>总个数:{{ userInfo.userInfo.link_amount }}个</span>
|
||||
<span>消耗个数:1个</span>
|
||||
</el-space>
|
||||
</div>
|
||||
<div class="main">
|
||||
<el-form
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
class="form-box margin-right-40"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item prop="title" label="卡片标题">
|
||||
<el-input v-model="formData.title" placeholder="请输入卡片标题"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="卡片描述">
|
||||
<el-input v-model="formData.description" placeholder="请输入卡片描述"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="卡片封面图">
|
||||
<div class="item-box">
|
||||
<oss-image
|
||||
class="margin-right-20"
|
||||
:paths="formData.icon ? [formData.icon] : []"
|
||||
:width="100"
|
||||
:height="100"
|
||||
></oss-image>
|
||||
<div>
|
||||
<div class="function-img">
|
||||
<upload-image
|
||||
class="margin-right-10"
|
||||
:type="['image/jpg', 'image/jpeg', 'image/png']"
|
||||
:size="1"
|
||||
v-model="formData.icon"
|
||||
></upload-image>
|
||||
<material-select v-model="formData.icon" class="margin-right-20"></material-select>
|
||||
</div>
|
||||
<span class="tip">只能上传jpg/png,建议大小100*100,且不超过1Mb</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="卡片备注">
|
||||
<el-input v-model="formData.remark" placeholder="请输入卡片备注,例如:xxx活动(选填)"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="config.domain_id" label="无风险域名">
|
||||
<el-select v-model="formData.config.domain_id" placeholder="请选择">
|
||||
<el-option v-for="item in domainList" :key="item.id" :label="item.title" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="config.url" label="优码二维码">
|
||||
<qr-link class="margin-b-10" v-model="formData.config.url"></qr-link>
|
||||
<el-input v-model="formData.config.url" disabled></el-input>
|
||||
<div style="font-size: 12px">
|
||||
<el-icon><QuestionFilled /></el-icon>
|
||||
上传在优码图片类使用微信二维码、公众号二维码、企业微信二维码、视频号二维码生成的二维码<br />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="show hidden-md-and-down">
|
||||
<el-divider>
|
||||
<span>抖音卡片预览</span>
|
||||
</el-divider>
|
||||
<div class="show-box">
|
||||
<oss-image
|
||||
class="margin-right-20"
|
||||
:paths="formData.icon ? [formData.icon] : []"
|
||||
:width="100"
|
||||
:height="100"
|
||||
></oss-image>
|
||||
<div class="show-des">
|
||||
<span class="show-des-t">{{ formData.title || '卡片标题' }}</span>
|
||||
<span class="show-des-b">{{ formData.description || '卡片描述' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.main {
|
||||
display: flex;
|
||||
.form-box {
|
||||
flex: 2;
|
||||
}
|
||||
.show {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.tip {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.show-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
border: 1px solid #f5f5f7;
|
||||
background-color: #f9fafe;
|
||||
}
|
||||
|
||||
.show-des {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
.show-des-t {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
}
|
||||
.show-des-b {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.item-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.function-img {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,120 @@
|
||||
<script setup lang="ts">
|
||||
import useCurd from '@/hooks/curd'
|
||||
import FormModal from '@/components/FormModal.vue'
|
||||
import CreateOrLook from './create-or-look.vue'
|
||||
import OssImage from '@/components/OssImage.vue'
|
||||
import { copyLink } from '@/utils'
|
||||
import { ApiRenewalLink } from '@/api/comment'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
|
||||
const user = useUserStore()
|
||||
const props = defineProps<{
|
||||
type: any
|
||||
}>()
|
||||
const { state, sizeChangeHandle, currentChangeHandle, showForm, submitForm, research, deleteHandle } = useCurd({
|
||||
url: '/links',
|
||||
queryForm: {
|
||||
type: props.type,
|
||||
page: 11,
|
||||
username: ''
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="8" :md="8" :lg="6" class="m-b-20">
|
||||
<el-input style="width: 100%" v-model="state.queryForm.title" placeholder="请输入标题" clearable></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="8" :md="8" :lg="6" class="m-b-20" v-if="user.userInfo.type === 3">
|
||||
<el-input
|
||||
style="width: 100%"
|
||||
placeholder="请输入用户名"
|
||||
v-model="state.queryForm.username"
|
||||
clearable
|
||||
></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="8" :md="8" :lg="6" class="m-b-20">
|
||||
<el-button type="primary" @click="research">搜索</el-button>
|
||||
<el-button type="primary" :plain="true" @click="showForm">创建卡片</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.listLoading"
|
||||
:header-cell-style="{
|
||||
'text-align': 'center',
|
||||
'background-color': '#F1F1F1',
|
||||
height: '50px',
|
||||
color: '#333'
|
||||
}"
|
||||
:cell-style="{ padding: '0', 'text-align': 'center', height: '80px', color: '#333' }"
|
||||
:row-key="state.primaryKey"
|
||||
>
|
||||
<el-table-column prop="id" label="序号"></el-table-column>
|
||||
<el-table-column label="图片">
|
||||
<template #default="{ row }">
|
||||
<oss-image :paths="row.icon ? [row.icon] : []" :width="60" :height="60" style="margin: 0 auto"></oss-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="卡片标题"></el-table-column>
|
||||
<el-table-column prop="description" label="卡片描述"></el-table-column>
|
||||
<el-table-column prop="" label="链接状态">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.status == 1">可用</span>
|
||||
<span v-else style="color: red">不可用</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="visit_uv_count" label="浏览量UV"></el-table-column>
|
||||
<el-table-column prop="expired_at" label="到期时间"></el-table-column>
|
||||
<el-table-column prop="user.username" label="所属用户"></el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="copyLink(row)">复制链接</el-button>
|
||||
<el-button type="primary" link @click="showForm(row.id)">编辑</el-button>
|
||||
<el-button type="primary" link @click="deleteHandle(row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
:current-page="state.page"
|
||||
:total="state.total"
|
||||
:page-sizes="state.pageSizes"
|
||||
:page-size="state.limit"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
/>
|
||||
<!-- 弹窗 -->
|
||||
<form-modal
|
||||
v-if="state.formVisible === true"
|
||||
v-model:visible="state.formVisible"
|
||||
width="1200"
|
||||
:style="{
|
||||
minWidth: '700px'
|
||||
}"
|
||||
type="dialog"
|
||||
:title="state.detailData?.id ? '编辑' : '新增'"
|
||||
:loading="state.formLoading"
|
||||
:detail="state.detailData"
|
||||
@submit="submitForm"
|
||||
cancelBtnName="取消"
|
||||
okBtnName="确定"
|
||||
>
|
||||
<create-or-look></create-or-look>
|
||||
</form-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.header-right {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
@@ -4,12 +4,14 @@ import WX from '@/views/link/addLink/components/wx/index.vue'
|
||||
import KingDoc from '@/views/link/addLink/components/kingDoc/index.vue'
|
||||
import QR from '@/views/link/addLink/components/qr/index.vue'
|
||||
import FirmWx from '@/views/link/addLink/components/firmWx/index.vue'
|
||||
import TengXun from '@/views/link/addLink/components/tengxun/index.vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import xcx from '@/assets/link/xcx.png'
|
||||
import jinshan from '@/assets/link/jinshan.png'
|
||||
import caoliao from '@/assets/link/caoliao.png'
|
||||
import qywx from '@/assets/link/qywx.png'
|
||||
import wx from '@/assets/link/wx.png'
|
||||
import tengxun from '@/assets/link/tengxun.png'
|
||||
import { userStore } from '@/stores'
|
||||
|
||||
const tabsList = [
|
||||
@@ -17,7 +19,9 @@ const tabsList = [
|
||||
{ id: 2, name: '跳转到金山文档', icon: jinshan },
|
||||
{ id: 3, name: '跳转到草料二维码', icon: caoliao },
|
||||
{ id: 4, name: '跳转到企业微信', icon: qywx },
|
||||
{ id: 1, name: '跳转到小程序', icon: xcx }
|
||||
{ id: 1, name: '跳转到小程序', icon: xcx },
|
||||
{ id: 11, name: '跳转到腾讯优码', icon: tengxun },
|
||||
|
||||
]
|
||||
const user = userStore
|
||||
// tabs点击事件
|
||||
@@ -53,6 +57,7 @@ onMounted(() => {
|
||||
<q-r v-if="type === 3" :type="type"></q-r>
|
||||
<firm-wx v-if="type === 4" :type="type"></firm-wx>
|
||||
<w-x v-if="type === 5" :type="type"></w-x>
|
||||
<teng-xun v-if="type === 11" :type="type"></teng-xun>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -3,12 +3,17 @@ import { onMounted, ref } from 'vue'
|
||||
import { ApiGetSetByType, ApiSaveSetByType } from '@/api/comment'
|
||||
import { ApiVipList } from '@/api/super'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import UploadImage from '@/components/uploadImage.vue'
|
||||
import MaterialSelect from '@/components/MaterialSelect/MaterialSelect.vue'
|
||||
import OssImage from '@/components/OssImage.vue'
|
||||
import { configStore } from '@/stores'
|
||||
|
||||
const formData = ref<any>({})
|
||||
|
||||
const vipPackages = ref<any>([])
|
||||
|
||||
const formLoading = ref(false)
|
||||
const webFormData = ref<any>({})
|
||||
|
||||
const clickSubmit = () => {
|
||||
formLoading.value = true
|
||||
@@ -20,6 +25,62 @@ const clickSubmit = () => {
|
||||
formLoading.value = false
|
||||
})
|
||||
}
|
||||
const saveSubmit = () => {
|
||||
if (webFormData.value.verify_code_is_open) {
|
||||
if (!webFormData.value.send_code_mode ){
|
||||
ElMessage.error('请选择发送方式')
|
||||
return
|
||||
}
|
||||
if (webFormData.value.send_code_mode === 1) {
|
||||
if (!webFormData.value.ali_sms_key) {
|
||||
ElMessage.error('请填写阿里短信key')
|
||||
return
|
||||
}
|
||||
if (!webFormData.value.ali_sms_secret) {
|
||||
ElMessage.error('请填写阿里短信secret')
|
||||
return
|
||||
}
|
||||
if (!webFormData.value.ali_sms_secret) {
|
||||
ElMessage.error('请填写阿里短信secret')
|
||||
return
|
||||
}
|
||||
} else if (webFormData.value === 2) {
|
||||
if (!webFormData.value.mail_from_name) {
|
||||
ElMessage.error('请填写发信名称')
|
||||
return
|
||||
}
|
||||
if (!webFormData.value.mail_host) {
|
||||
ElMessage.error('请填写服务器地址')
|
||||
return
|
||||
}
|
||||
if (!webFormData.value.mail_port) {
|
||||
ElMessage.error('请填写端口')
|
||||
return
|
||||
}
|
||||
if (!webFormData.value.mail_from_address) {
|
||||
ElMessage.error('请填写发信地址')
|
||||
return
|
||||
}
|
||||
if (!webFormData.value.mail_username) {
|
||||
ElMessage.error('请填写邮箱账号')
|
||||
return
|
||||
}
|
||||
if (!webFormData.value.mail_password) {
|
||||
ElMessage.error('请填写邮箱密码')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
formLoading.value = true
|
||||
ApiSaveSetByType('web_site', webFormData.value)
|
||||
.then(() => {
|
||||
ElMessage.success('保存成功')
|
||||
configStore.refreshConfig()
|
||||
})
|
||||
.finally(() => {
|
||||
formLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
formLoading.value = true
|
||||
@@ -30,6 +91,9 @@ onMounted(() => {
|
||||
.finally(() => {
|
||||
formLoading.value = false
|
||||
})
|
||||
ApiGetSetByType('web_site').then((res: any) => {
|
||||
webFormData.value = res
|
||||
})
|
||||
ApiVipList().then((res: any) => {
|
||||
vipPackages.value = vipPackages.value.concat(res)
|
||||
})
|
||||
@@ -37,43 +101,218 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card header="网站设置" v-loading="formLoading">
|
||||
<el-form label-width="200px" :disabled="formLoading">
|
||||
<div>
|
||||
<el-card title="基础配置">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<div class="m-b-20">顶部LOGO图片信息</div>
|
||||
<div class="item-box">
|
||||
<oss-image
|
||||
class="margin-right-20"
|
||||
:paths="webFormData.web_site_logo ? [webFormData.web_site_logo] : []"
|
||||
:width="100"
|
||||
:height="100"
|
||||
></oss-image>
|
||||
<div>
|
||||
<div class="function-img m-b-20">
|
||||
<upload-image
|
||||
class="margin-right-10"
|
||||
:type="['image/jpg', 'image/jpeg', 'image/png']"
|
||||
:size="1"
|
||||
v-model="webFormData.web_site_logo"
|
||||
></upload-image>
|
||||
<material-select
|
||||
v-model="webFormData.web_site_logo"
|
||||
class="margin-right-20"
|
||||
></material-select>
|
||||
</div>
|
||||
<span class="tip">只能上传jpg/png,建议大小100*100,且不超过1Mb</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<div class="m-b-20">底部LOGO图片信息</div>
|
||||
<div class="item-box">
|
||||
<oss-image
|
||||
class="margin-right-20"
|
||||
:paths="webFormData.web_site_bottom_logo ? [webFormData.web_site_bottom_logo] : []"
|
||||
:width="100"
|
||||
:height="100"
|
||||
></oss-image>
|
||||
<div>
|
||||
<div class="function-img m-b-20">
|
||||
<upload-image
|
||||
class="margin-right-10"
|
||||
:type="['image/jpg', 'image/jpeg', 'image/png']"
|
||||
:size="1"
|
||||
v-model="webFormData.web_site_bottom_logo"
|
||||
></upload-image>
|
||||
<material-select v-model="webFormData.web_site_bottom_logo" class="margin-right-20"></material-select>
|
||||
</div>
|
||||
<span class="tip">只能上传jpg/png,且不超过1Mb</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<div class="m-b-20">上传客服二维码</div>
|
||||
<div class="item-box">
|
||||
<oss-image
|
||||
class="margin-right-20"
|
||||
:paths="webFormData.web_site_customer_service ? [webFormData.web_site_customer_service] : []"
|
||||
:width="100"
|
||||
:height="100"
|
||||
></oss-image>
|
||||
<div>
|
||||
<div class="function-img m-b-20">
|
||||
<upload-image
|
||||
class="margin-right-10"
|
||||
:type="['image/jpg', 'image/jpeg', 'image/png']"
|
||||
:size="1"
|
||||
v-model="webFormData.web_site_customer_service"
|
||||
></upload-image>
|
||||
<material-select
|
||||
v-model="webFormData.web_site_customer_service"
|
||||
class="margin-right-20"
|
||||
></material-select>
|
||||
</div>
|
||||
<span class="tip">只能上传jpg/png,建议大小100*100,且不超过1Mb</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-form-item label="注册赠送会员">
|
||||
<el-select v-model="formData.give_vip_id" placeholder="注册赠送会员">
|
||||
<el-option style="width: 200px" v-for="vip in vipPackages" :key="vip.id" :label="vip.name" :value="vip.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否开启赠送会员">
|
||||
<el-switch style="width: 200px" v-model="formData.is_give_vip" ></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="注册赠送天数">
|
||||
<el-input-number style="width: 200px" v-model="formData.give_vip_days" :min="1" :max="365" />
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码发送方式">
|
||||
<el-select style="width: 200px" v-model="formData.send_code_mode" placeholder="请选择验证码发送方式">
|
||||
<el-option label="短信" :value="1" />
|
||||
<el-option label="邮箱" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="一级邀请注册佣金">
|
||||
<el-input-number style="width: 200px" v-model="formData.recommend_commission_1" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="二级邀请注册佣金">
|
||||
<el-input-number style="width: 200px" v-model="formData.recommend_commission_2" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="一级分销(%)">
|
||||
<el-input-number style="width: 200px" v-model="formData.member_pay_commission_1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="二级分销(%)">
|
||||
<el-input-number style="width: 200px" v-model="formData.member_pay_commission_2" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">网站标题</div>
|
||||
<el-input v-model="webFormData.web_site_title" placeholder="请输入网站标题"></el-input>
|
||||
</el-col>
|
||||
<el-divider content-position="left">验证码</el-divider>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">是否开启验证码</div>
|
||||
<el-switch style="width: 100%" :active-value="1" :inactive-value="0" v-model="webFormData.verify_code_is_open"></el-switch>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.verify_code_is_open">
|
||||
<div class="margin-b-10" style="margin-top: 20px">验证码发送方式</div>
|
||||
<el-select style="width: 100%" v-model="webFormData.send_code_mode" placeholder="请选择验证码发送方式">
|
||||
<el-option label="短信" :value="1" />
|
||||
<el-option label="邮箱" :value="2" />
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 1">
|
||||
<div class="margin-b-10" style="margin-top: 20px">阿里短信key</div>
|
||||
<el-input v-model="webFormData.ali_sms_key" placeholder="请输入阿里短信key"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 1">
|
||||
<div class="margin-b-10" style="margin-top: 20px">阿里短信secret</div>
|
||||
<el-input v-model="webFormData.ali_sms_secret" placeholder="请输入阿里短信secret"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 1">
|
||||
<div class="margin-b-10" style="margin-top: 20px">阿里短信签名</div>
|
||||
<el-input v-model="webFormData.ali_sms_sign_name" placeholder="请输入阿里短信签名"></el-input>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 2">
|
||||
<div class="margin-b-10" style="margin-top: 20px">发信名称</div>
|
||||
<el-input v-model="webFormData.mail_from_name" placeholder="请输入发信名称"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 2">
|
||||
<div class="margin-b-10" style="margin-top: 20px">服务器地址</div>
|
||||
<el-input v-model="webFormData.mail_host" placeholder="请输入服务器地址"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 2">
|
||||
<div class="margin-b-10" style="margin-top: 20px">端口</div>
|
||||
<el-input v-model="webFormData.mail_port" placeholder="端口"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 2">
|
||||
<div class="margin-b-10" style="margin-top: 20px">发信地址</div>
|
||||
<el-input v-model="webFormData.mail_from_address" placeholder="请输入发信地址"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 2">
|
||||
<div class="margin-b-10" style="margin-top: 20px">发信账号</div>
|
||||
<el-input v-model="webFormData.mail_username" placeholder="请输入发信账号"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6" v-if="webFormData.send_code_mode === 2">
|
||||
<div class="margin-b-10" style="margin-top: 20px">发信密码</div>
|
||||
<el-input v-model="webFormData.mail_password" placeholder="请输入发信密码"></el-input>
|
||||
</el-col>
|
||||
|
||||
<el-divider content-position="left"> 支付配置</el-divider>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信商户appId</div>
|
||||
<el-input v-model="webFormData.wechat_pay_app_id" placeholder="请输入微信商户appId"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信商户号</div>
|
||||
<el-input v-model="webFormData.wechat_pay_mch_id" placeholder="请输入微信商户号"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信支付密钥</div>
|
||||
<el-input v-model="webFormData.wechat_pay_secret_key" placeholder="请输入微信支付密钥"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信支付私钥证书</div>
|
||||
<el-input v-model="webFormData.wechat_pay_private_cert" placeholder="请输入微信支付私钥证书"></el-input>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信支付公钥证书</div>
|
||||
<el-input v-model="webFormData.wechat_pay_certificate" placeholder="请输入微信支付公钥证书"></el-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="text-center m-t-30">
|
||||
<el-button type="primary" style="width: 200px" @click="saveSubmit" :loading="formLoading">保存</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card v-loading="formLoading" title="分销配置" class="m-t-20">
|
||||
<el-form label-width="200px" :disabled="formLoading">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">是否开启赠送会员</div>
|
||||
<el-switch style="width: 100%" v-model="formData.is_give_vip"></el-switch>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">注册赠送会员</div>
|
||||
|
||||
<el-select v-model="formData.give_vip_id" placeholder="注册赠送会员" style="width: 100%">
|
||||
<el-option v-for="vip in vipPackages" :key="vip.id" :label="vip.name" :value="vip.id" />
|
||||
</el-select>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">注册赠送天数</div>
|
||||
<el-input-number style="width: 100%" v-model="formData.give_vip_days" :min="1" :max="365" />
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">一级邀请注册佣金</div>
|
||||
<el-input-number style="width: 100%" v-model="formData.recommend_commission_1" :min="0" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">二级邀请注册佣金</div>
|
||||
<el-input-number style="width: 100%" v-model="formData.recommend_commission_2" :min="0" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">一级分销(%)</div>
|
||||
<el-input-number style="width: 100%" v-model="formData.member_pay_commission_1" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="margin-b-10" style="margin-top: 20px">二级分销(%)</div>
|
||||
<el-input-number style="width: 100%" v-model="formData.member_pay_commission_2" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="text-center m-t-30">
|
||||
<el-button type="primary" style="width: 200px" @click="clickSubmit" :loading="formLoading">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.item-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.function-img {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue","./public/config.json"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
@@ -9,6 +9,7 @@
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"types": ["element-plus/global"],
|
||||
"moduleResolution": "node"
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user