Merge branch 'develop' of https://gitee.com/likeadmin/likeadmin_java into develop
|
|
@ -0,0 +1,11 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 用户列表
|
||||
export function getUserList(params: any) {
|
||||
return request.get({ url: '/user/list', params })
|
||||
}
|
||||
|
||||
// 用户详情
|
||||
export function getUserDetail(params: any) {
|
||||
return request.get({ url: '/user/detail', params })
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
>
|
||||
<del-wrap @close="deleteImg(index)">
|
||||
<file-item
|
||||
:uri="element"
|
||||
:uri="excludeDomain ? getImageUrl(element) : element"
|
||||
:file-size="size"
|
||||
:type="type"
|
||||
></file-item>
|
||||
|
|
@ -82,6 +82,7 @@ import Popup from '@/components/popup/index.vue'
|
|||
import FileItem from './file.vue'
|
||||
import Material from './index.vue'
|
||||
import Preview from './preview.vue'
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Popup,
|
||||
|
|
@ -128,6 +129,11 @@ export default defineComponent({
|
|||
uploadClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
//选择的url排出域名
|
||||
excludeDomain: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -142,6 +148,7 @@ export default defineComponent({
|
|||
const isAdd = ref(true)
|
||||
const currentIndex = ref(-1)
|
||||
const { disabled, limit, modelValue } = toRefs(props)
|
||||
const { getImageUrl } = useAppStore()
|
||||
const tipsText = computed(() => {
|
||||
switch (props.type) {
|
||||
case 'image':
|
||||
|
|
@ -164,7 +171,9 @@ export default defineComponent({
|
|||
return limit.value - fileList.value.length
|
||||
})
|
||||
const handleConfirm = () => {
|
||||
const selectUri = select.value.map((item) => item.uri)
|
||||
const selectUri = select.value.map((item) =>
|
||||
props.excludeDomain ? item.path : item.uri
|
||||
)
|
||||
if (!isAdd.value) {
|
||||
fileList.value.splice(currentIndex.value, 1, selectUri.shift())
|
||||
} else {
|
||||
|
|
@ -234,7 +243,8 @@ export default defineComponent({
|
|||
previewUrl,
|
||||
showPreview,
|
||||
handlePreview,
|
||||
handleClose
|
||||
handleClose,
|
||||
getImageUrl
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -19,3 +19,22 @@ export enum ScreenEnum {
|
|||
XL = 1280,
|
||||
'2XL' = 1536
|
||||
}
|
||||
|
||||
// 客户端类型
|
||||
export enum ClientEnum {
|
||||
MP_WEIXIN = 1, // 微信-小程序
|
||||
OA_WEIXIN = 2, // 微信-公众号
|
||||
H5 = 3, // H5
|
||||
PC = 4, // PC
|
||||
IOS = 5, //苹果
|
||||
ANDROID = 6 //安卓
|
||||
}
|
||||
|
||||
export const ClientMap = {
|
||||
[ClientEnum.MP_WEIXIN]: '微信小程序',
|
||||
[ClientEnum.OA_WEIXIN]: '微信公众号',
|
||||
[ClientEnum.H5]: '手机H5Z',
|
||||
[ClientEnum.PC]: '电脑PC',
|
||||
[ClientEnum.IOS]: '苹果APP',
|
||||
[ClientEnum.ANDROID]: '安卓APP'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ const useAppStore = defineStore({
|
|||
}
|
||||
},
|
||||
actions: {
|
||||
getImageUrl(url: string) {
|
||||
return url ? `${this.config.ossDomain}${url}` : ''
|
||||
},
|
||||
getConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getConfig()
|
||||
|
|
|
|||
|
|
@ -18,8 +18,12 @@
|
|||
<el-input v-model="formData.title" placeholder="请输入文章标题" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="文章标题" prop="cid">
|
||||
<el-select class="w-80" v-model="formData.cid">
|
||||
<el-form-item label="文章栏目" prop="cid">
|
||||
<el-select
|
||||
class="w-80"
|
||||
v-model="formData.cid"
|
||||
placeholder="请选择文章栏目"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in optionsData.articleCate"
|
||||
:key="item.id"
|
||||
|
|
@ -107,15 +111,8 @@ const formData = reactive({
|
|||
|
||||
const formRef = shallowRef<FormInstance>()
|
||||
const rules = reactive({
|
||||
['base.tableName']: [{ required: true, message: '请输入表名称', trigger: 'blur' }],
|
||||
['base.tableComment']: [{ required: true, message: '请输入表描述', trigger: 'blur' }],
|
||||
['base.entityName']: [{ required: true, message: '请输入实体类名称', trigger: 'blur' }],
|
||||
['base.authorName']: [{ required: true, message: '请输入作者', trigger: 'blur' }],
|
||||
['gen.moduleName']: [{ required: true, message: '请输入模块名', trigger: 'blur' }],
|
||||
['gen.functionName']: [{ required: true, message: '请输入功能名称', trigger: 'blur' }],
|
||||
['gen.treePrimary']: [{ required: true, message: '请选择树主键字段', trigger: 'blur' }],
|
||||
['gen.treeParent']: [{ required: true, message: '请选择树父级字段', trigger: 'blur' }],
|
||||
['gen.treeName']: [{ required: true, message: '请选择树名称字段', trigger: 'blur' }]
|
||||
title: [{ required: true, message: '请输入文章标题', trigger: 'blur' }],
|
||||
cid: [{ required: true, message: '请输入表描述', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const getDetails = async () => {
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@
|
|||
<el-table-column label="ID" prop="id" min-width="80" />
|
||||
<el-table-column label="封面" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<image-contain
|
||||
<el-image
|
||||
v-if="row.image"
|
||||
:src="row.image"
|
||||
:width="60"
|
||||
height="45"
|
||||
class="w-[60px] h-[45px]"
|
||||
:preview-src-list="[row.image]"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,73 +1,51 @@
|
|||
<template>
|
||||
<div class="article-edit">
|
||||
<div>
|
||||
<el-card class="!border-none" shadow="never">
|
||||
<el-page-header content="用户详情" @back="$router.back()" />
|
||||
</el-card>
|
||||
<el-card class="mt-4 !border-none" header="基本资料" shadow="never">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
class="ls-form"
|
||||
:model="formData"
|
||||
label-width="120px"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-form ref="formRef" class="ls-form" :model="formData" label-width="120px">
|
||||
<div class="bg-page py-5 pl-20 mb-10">
|
||||
<div class="mb-3 text-tx-regular">用户头像</div>
|
||||
<el-avatar :size="58" />
|
||||
<el-avatar :src="formData.avatar" :size="58" />
|
||||
</div>
|
||||
<el-form-item label="用户编号:"> 20000440556 </el-form-item>
|
||||
<el-form-item label="用户昵称:"> 热心市民小张 </el-form-item>
|
||||
<el-form-item label="账号:"> fwewrwre90 </el-form-item>
|
||||
<el-form-item label="真实姓名:"> 张三 </el-form-item>
|
||||
<el-form-item label="性别:"> 未知 </el-form-item>
|
||||
<el-form-item label="联系电话:"> 13800138000 </el-form-item>
|
||||
<el-form-item label="注册来源:"> 微信小程序 </el-form-item>
|
||||
<el-form-item label="注册时间:"> 2022-02-15 12:12:12 </el-form-item>
|
||||
<el-form-item label="最近登录时间:"> 2022-02-18 12:12:12 </el-form-item>
|
||||
<el-form-item label="用户编号:"> {{ formData.sn }} </el-form-item>
|
||||
<el-form-item label="用户昵称:"> {{ formData.nickname }} </el-form-item>
|
||||
<el-form-item label="账号:"> {{ formData.username }} </el-form-item>
|
||||
<el-form-item label="真实姓名:"> {{ formData.realName || '-' }} </el-form-item>
|
||||
<el-form-item label="性别:"> {{ formData.sex }} </el-form-item>
|
||||
<el-form-item label="联系电话:"> {{ formData.mobile || '-' }} </el-form-item>
|
||||
<el-form-item label="注册来源:"> {{ formData.channel }} </el-form-item>
|
||||
<el-form-item label="注册时间:"> {{ formData.createTime }} </el-form-item>
|
||||
<el-form-item label="最近登录时间:"> {{ formData.lastLoginTime }} </el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<footer-btns>
|
||||
<el-button type="primary" @click="handleSave">保存</el-button>
|
||||
</footer-btns>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import feedback from '@/utils/feedback'
|
||||
import { useDictOptions } from '@/hooks/useDictOptions'
|
||||
import { articleCateAll, articleDetail, articleEdit } from '@/api/article'
|
||||
import { getUserDetail } from '@/api/consumer'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
title: '',
|
||||
image: '',
|
||||
cid: '',
|
||||
intro: '',
|
||||
author: '',
|
||||
content: '',
|
||||
visit: 0,
|
||||
sort: 0,
|
||||
isShow: ''
|
||||
avatar: '',
|
||||
channel: '',
|
||||
createTime: '',
|
||||
lastLoginIp: '',
|
||||
lastLoginTime: '',
|
||||
mobile: '',
|
||||
nickname: '',
|
||||
realName: 0,
|
||||
sex: 0,
|
||||
sn: '',
|
||||
username: ''
|
||||
})
|
||||
|
||||
const formRef = shallowRef<FormInstance>()
|
||||
const rules = reactive({
|
||||
['base.tableName']: [{ required: true, message: '请输入表名称', trigger: 'blur' }],
|
||||
['base.tableComment']: [{ required: true, message: '请输入表描述', trigger: 'blur' }],
|
||||
['base.entityName']: [{ required: true, message: '请输入实体类名称', trigger: 'blur' }],
|
||||
['base.authorName']: [{ required: true, message: '请输入作者', trigger: 'blur' }],
|
||||
['gen.moduleName']: [{ required: true, message: '请输入模块名', trigger: 'blur' }],
|
||||
['gen.functionName']: [{ required: true, message: '请输入功能名称', trigger: 'blur' }],
|
||||
['gen.treePrimary']: [{ required: true, message: '请选择树主键字段', trigger: 'blur' }],
|
||||
['gen.treeParent']: [{ required: true, message: '请选择树父级字段', trigger: 'blur' }],
|
||||
['gen.treeName']: [{ required: true, message: '请选择树名称字段', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const getDetails = async () => {
|
||||
const data = await articleDetail({
|
||||
const data = await getUserDetail({
|
||||
id: route.query.id
|
||||
})
|
||||
Object.keys(formData).forEach((key) => {
|
||||
|
|
@ -76,20 +54,5 @@ const getDetails = async () => {
|
|||
})
|
||||
}
|
||||
|
||||
const { optionsData } = useDictOptions<{
|
||||
articleCate: any[]
|
||||
}>({
|
||||
articleCate: {
|
||||
api: articleCateAll
|
||||
}
|
||||
})
|
||||
|
||||
const handleSave = async () => {
|
||||
await formRef.value?.validate()
|
||||
await articleEdit(formData)
|
||||
feedback.msgSuccess('操作成功')
|
||||
router.back()
|
||||
}
|
||||
|
||||
getDetails()
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -5,26 +5,24 @@
|
|||
<el-form-item label="用户信息">
|
||||
<el-input
|
||||
class="w-56"
|
||||
v-model="queryParams.title"
|
||||
v-model="queryParams.keyword"
|
||||
placeholder="用户编号/昵称/手机号码"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="注册时间">
|
||||
<el-select class="w-56" v-model="queryParams.cid">
|
||||
<el-option label="全部" value />
|
||||
<el-option
|
||||
v-for="item in optionsData.articleCate"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<daterange-picker
|
||||
v-model:startTime="queryParams.startTime"
|
||||
v-model:endTime="queryParams.endTime"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="注册来源">
|
||||
<el-select class="w-56" v-model="queryParams.isShow">
|
||||
<el-option label="全部" value />
|
||||
<el-option label="显示" :value="1" />
|
||||
<el-option label="隐藏" :value="0" />
|
||||
<el-select class="w-56" v-model="queryParams.channel">
|
||||
<el-option
|
||||
v-for="(item, key) in ClientMap"
|
||||
:key="key"
|
||||
:label="item"
|
||||
:value="key"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
|
|
@ -35,33 +33,17 @@
|
|||
</el-card>
|
||||
<el-card class="!border-none mt-4" shadow="never">
|
||||
<el-table size="large" v-loading="pager.loading" :data="pager.lists">
|
||||
<el-table-column label="用户编号" prop="id" min-width="80" />
|
||||
<el-table-column label="用户编号" prop="sn" min-width="120" />
|
||||
<el-table-column label="头像" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<image-contain
|
||||
:src="row.image"
|
||||
:width="60"
|
||||
height="45"
|
||||
:preview-src-list="[row.image]"
|
||||
/>
|
||||
<el-avatar :src="row.image" :size="50" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="昵称" prop="category" min-width="100" />
|
||||
<el-table-column label="账号" prop="author" min-width="120" />
|
||||
<el-table-column label="手机号码" prop="visit" min-width="100" />
|
||||
<el-table-column label="性别" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-perms="['article:cate:change']"
|
||||
v-if="row.id != 1"
|
||||
v-model="row.isShow"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
@change="changeStatus(row.id)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="注册来源" prop="sort" min-width="100" />
|
||||
<el-table-column label="昵称" prop="nickname" min-width="100" />
|
||||
<el-table-column label="账号" prop="username" min-width="120" />
|
||||
<el-table-column label="手机号码" prop="mobile" min-width="100" />
|
||||
<el-table-column label="性别" prop="sex" min-width="100" />
|
||||
<el-table-column label="注册来源" prop="channel" min-width="100" />
|
||||
<el-table-column label="注册时间" prop="createTime" min-width="120" />
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="{ row }">
|
||||
|
|
@ -87,46 +69,21 @@
|
|||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { articleLists, articleDelete, articleStatus, articleCateAll } from '@/api/article'
|
||||
import { useDictOptions } from '@/hooks/useDictOptions'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import feedback from '@/utils/feedback'
|
||||
import { getRoutePath } from '@/router'
|
||||
import { getUserList } from '@/api/consumer'
|
||||
import { ClientMap } from '@/enums/appEnums'
|
||||
const queryParams = reactive({
|
||||
title: '',
|
||||
cid: '',
|
||||
isShow: ''
|
||||
keyword: '',
|
||||
channel: '',
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
})
|
||||
|
||||
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||
fetchFun: articleLists,
|
||||
fetchFun: getUserList,
|
||||
params: queryParams
|
||||
})
|
||||
|
||||
const { optionsData } = useDictOptions<{
|
||||
articleCate: any[]
|
||||
}>({
|
||||
articleCate: {
|
||||
api: articleCateAll
|
||||
}
|
||||
})
|
||||
|
||||
const changeStatus = async (id: number) => {
|
||||
try {
|
||||
await articleStatus({ id })
|
||||
feedback.msgSuccess('修改成功')
|
||||
getLists()
|
||||
} catch (error) {
|
||||
getLists()
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
await feedback.confirm('确定要删除?')
|
||||
await articleDelete({ id })
|
||||
feedback.msgSuccess('删除成功')
|
||||
getLists()
|
||||
}
|
||||
|
||||
getLists()
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@
|
|||
@close="handleDelete(index)"
|
||||
>
|
||||
<div class="bg-fill-light flex items-center w-full p-4 mb-4">
|
||||
<material-picker v-model="item.image" upload-class="bg-body" size="60px">
|
||||
<material-picker
|
||||
v-model="item.image"
|
||||
upload-class="bg-body"
|
||||
size="60px"
|
||||
exclude-domain
|
||||
>
|
||||
<template #upload>
|
||||
<div class="upload-btn w-[60px] h-[60px]">
|
||||
<icon name="el-icon-Plus" :size="20" />
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="图片设置">
|
||||
<div class="flex-1">
|
||||
<div class="form-tips">最多添加5张,建议图片尺寸:750px*400px</div>
|
||||
<div class="form-tips">最多添加5张,建议图片尺寸:750px*240px</div>
|
||||
<del-wrap
|
||||
v-for="(item, index) in content.data"
|
||||
:key="index"
|
||||
|
|
@ -17,7 +17,11 @@
|
|||
class="max-w-[400px]"
|
||||
>
|
||||
<div class="bg-fill-light flex items-center w-full p-4 mt-4">
|
||||
<material-picker v-model="item.image" upload-class="bg-body" />
|
||||
<material-picker
|
||||
v-model="item.image"
|
||||
upload-class="bg-body"
|
||||
exclude-domain
|
||||
/>
|
||||
<div class="ml-3 flex-1">
|
||||
<el-form-item label="图片名称">
|
||||
<el-input v-model="item.name" placeholder="请输入名称" />
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
<template>
|
||||
<div class="banner">
|
||||
<div class="banner-image">
|
||||
<image-contain width="100%" height="200px" :src="getImage" fit="contain" />
|
||||
<image-contain width="100%" height="170px" :src="getImageUrl(getImage)" fit="contain" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import type { PropType } from 'vue'
|
||||
import type options from './options'
|
||||
type OptionsType = ReturnType<typeof options>
|
||||
|
|
@ -19,6 +20,7 @@ const props = defineProps({
|
|||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
const getImage = computed(() => {
|
||||
const { data } = props.content
|
||||
if (Array.isArray(data)) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="客服二维码">
|
||||
<div>
|
||||
<material-picker v-model="content.qrcode" />
|
||||
<material-picker v-model="content.qrcode" exclude-domain />
|
||||
<div class="form-tips">建议图片尺寸:200*200像素;图片格式:jpg、png、jpeg</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="customer-service">
|
||||
<image-contain width="140px" height="140px" :src="content.qrcode" alt="" />
|
||||
<image-contain width="140px" height="140px" :src="getImageUrl(content.qrcode)" alt="" />
|
||||
<div class="text-[15px] mt-[7px] font-medium">{{ content.title }}</div>
|
||||
<div class="text-[#666] mt-[20px]">服务时间:{{ content.time }}</div>
|
||||
<div class="text-[#666] mt-[7px]">客服电话:{{ content.mobile }}</div>
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import type { PropType } from 'vue'
|
||||
import type options from './options'
|
||||
type OptionsType = ReturnType<typeof options>
|
||||
|
|
@ -25,6 +26,7 @@ defineProps({
|
|||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
:key="index"
|
||||
class="flex items-center border-b border-[#e5e5e5] h-[50px] px-[12px]"
|
||||
>
|
||||
<image-contain width="24px" height="24px" :src="item.image" alt="" />
|
||||
<image-contain width="24px" height="24px" :src="getImageUrl(item.image)" alt="" />
|
||||
<div class="ml-[10px] flex-1">{{ item.name }}</div>
|
||||
<div>
|
||||
<icon name="el-icon-ArrowRight" />
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import type { PropType } from 'vue'
|
||||
import type options from './options'
|
||||
type OptionsType = ReturnType<typeof options>
|
||||
|
|
@ -42,6 +43,7 @@ defineProps({
|
|||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@
|
|||
:key="index"
|
||||
class="flex flex-col items-center w-1/5 mb-[15px]"
|
||||
>
|
||||
<image-contain width="41px" height="41px" :src="item.image" alt="" />
|
||||
<image-contain width="41px" height="41px" :src="getImageUrl(item.image)" alt="" />
|
||||
<div class="mt-[7px]">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import type { PropType } from 'vue'
|
||||
import type options from './options'
|
||||
type OptionsType = ReturnType<typeof options>
|
||||
|
|
@ -26,6 +27,7 @@ const props = defineProps({
|
|||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@
|
|||
class="max-w-[400px]"
|
||||
>
|
||||
<div class="bg-fill-light flex items-center w-full p-4 mt-4">
|
||||
<material-picker v-model="item.image" upload-class="bg-body" />
|
||||
<material-picker
|
||||
v-model="item.image"
|
||||
upload-class="bg-body"
|
||||
exclude-domain
|
||||
/>
|
||||
<div class="ml-3 flex-1">
|
||||
<el-form-item label="图片名称">
|
||||
<el-input v-model="item.name" placeholder="请输入名称" />
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
<template>
|
||||
<div class="banner mx-[10px] mt-[10px]">
|
||||
<div class="banner-image">
|
||||
<image-contain width="100%" height="100px" :src="getImage" fit="contain" />
|
||||
<image-contain width="100%" height="100px" :src="getImageUrl(getImage)" fit="contain" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import type { PropType } from 'vue'
|
||||
import type options from './options'
|
||||
type OptionsType = ReturnType<typeof options>
|
||||
|
|
@ -19,6 +20,7 @@ const props = defineProps({
|
|||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
const getImage = computed(() => {
|
||||
const { data } = props.content
|
||||
if (Array.isArray(data)) {
|
||||
|
|
|
|||
|
|
@ -12,12 +12,5 @@
|
|||
height: 115px;
|
||||
background-position: bottom;
|
||||
background-size: 100% auto;
|
||||
.search-con {
|
||||
height: 100%;
|
||||
height: 36px;
|
||||
border-radius: 36px;
|
||||
background: #f4f4f4;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<template>
|
||||
<div class="website-filing">
|
||||
<el-card shadow="never" class="!border-none">
|
||||
<div class="mb-5">底部版权设置</div>
|
||||
<div class="mb-5">站点底部版权备案信息设置</div>
|
||||
<el-form ref="form" class="ls-form" label-width="100px">
|
||||
<del-wrap
|
||||
v-for="(item, index) in formData"
|
||||
|
|
@ -12,10 +12,13 @@
|
|||
@close="handleDelete(index)"
|
||||
>
|
||||
<div class="bg-fill-lighter py-4">
|
||||
<el-form-item label="显示名称" prop="name">
|
||||
<el-form-item label="显示内容" prop="name">
|
||||
<div class="w-80">
|
||||
<div>
|
||||
<el-input v-model="item.name" placeholder="请输入名称" />
|
||||
<el-input
|
||||
v-model="item.name"
|
||||
placeholder="请输入内容 ,例如: ICP备案号"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
<script setup lang="ts">
|
||||
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
|
||||
import request from './utils/request'
|
||||
import { onLaunch } from '@dcloudio/uni-app'
|
||||
import { useAppStore } from './stores/app'
|
||||
import { useUserStore } from './stores/user'
|
||||
const { getConfig } = useAppStore()
|
||||
const { getUser } = useUserStore()
|
||||
console.log(useUserStore())
|
||||
onLaunch(() => {
|
||||
console.log('App Launch')
|
||||
})
|
||||
onShow(() => {
|
||||
console.log('App Show')
|
||||
})
|
||||
onHide(() => {
|
||||
console.log('App Hide')
|
||||
getConfig()
|
||||
getUser()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
//注册
|
||||
export function smsSend(data: Record<string, any>) {
|
||||
//发送短信
|
||||
export function smsSend(data: any) {
|
||||
return request.post({ url: '/sms/send', data: data })
|
||||
}
|
||||
|
||||
export function getConfig() {
|
||||
return request.get({ url: '/config' })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* @description 获取文章分类
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getArticleCate() {
|
||||
return request.get({ url: '/article/category' })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取文章列表
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getArticleList(data: Record<string, any>) {
|
||||
return request.get({ url: '/article/list', data: data })
|
||||
}
|
||||
|
|
@ -4,3 +4,8 @@ import request from '@/utils/request'
|
|||
export function getIndex() {
|
||||
return request.get({ url: '/index' })
|
||||
}
|
||||
|
||||
// 装修页面
|
||||
export function getDecorate(data: any) {
|
||||
return request.get({ url: '/decorate', data })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getUserCenter() {
|
||||
return request.get({ url: '/user/center' })
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<template>
|
||||
<view>
|
||||
<view v-for="(item, index) in pages" :key="index">
|
||||
<template v-if="item.name == 'search'">
|
||||
<w-search :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
<template v-if="item.name == 'banner'">
|
||||
<w-banner :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
<template v-if="item.name == 'nav'">
|
||||
<w-nav :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
|
||||
defineProps({
|
||||
pages: {
|
||||
type: Array as PropType<any[]>
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<navigator :url="`/pages/news_detail/news_detail?id=${item.id}`">
|
||||
<view class="news-card flex bg-white px-[20rpx] py-[32rpx]">
|
||||
<view class="mr-[20rpx]" v-if="item.image">
|
||||
<u-image :src="item.image" width="240" height="180"></u-image>
|
||||
</view>
|
||||
<view class="news-card-content flex flex-col justify-between flex-1">
|
||||
<view class="news-card-content-title text-lg font-medium">{{ item.title }}</view>
|
||||
<view class="news-card-content-intro text-gray-400 text-sm mt-[16rpx] ">{{ item.intro }}</view>
|
||||
|
||||
<view class="text-muted text-xs w-full flex justify-between mt-[12rpx]">
|
||||
<view>{{ item.createTime }}</view>
|
||||
<view class="flex items-center">
|
||||
<image src="/static/images/icon_visit.png" class="w-[30rpx] h-[30rpx]"></image>
|
||||
<view class="ml-[10rpx]">{{ item.visit }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</navigator>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const props = withDefaults(defineProps < {
|
||||
item: any
|
||||
} > (), {
|
||||
item: {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-card {
|
||||
border-bottom: 1px solid #f8f8f8;
|
||||
&-content {
|
||||
&-title {
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
&-intro {
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,19 +1,29 @@
|
|||
<template>
|
||||
<view class="banner h-[400rpx] bg-white">
|
||||
<view
|
||||
class="banner h-[340rpx] bg-white translate-y-0"
|
||||
v-if="content.data.length && content.enabled"
|
||||
>
|
||||
<swiper
|
||||
class="swiper h-full"
|
||||
indicator-dots
|
||||
:indicator-dots="content.data.length > 1"
|
||||
indicator-active-color="#4173ff"
|
||||
:autoplay="true"
|
||||
>
|
||||
<swiper-item v-for="(item, index) in content.data" :key="index">
|
||||
<u-image mode="aspectFit" width="100%" height="100%" :src="item.image" />
|
||||
<u-image
|
||||
mode="aspectFit"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:src="getImageUrl(item.image)"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
|
|
@ -24,6 +34,7 @@ const props = defineProps({
|
|||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<view
|
||||
class="customer-service bg-white flex flex-col justify-center items-center mx-[36rpx] mt-[20rpx] rounded-lg px-[110rpx] pt-[100rpx] pb-[160rpx]"
|
||||
>
|
||||
<u-image width="280" height="280" :src="getImageUrl(content.qrcode)" />
|
||||
<view v-if="content.title" class="text-lg mt-[14rpx] font-medium">{{ content.title }}</view>
|
||||
<view v-if="content.time" class="text-content mt-[40rpx]"
|
||||
>服务时间:{{ content.time }}</view
|
||||
>
|
||||
<view v-if="content.mobile" class="text-content mt-[14rpx] flex flex-wrap">
|
||||
客服电话:{{ content.mobile }}
|
||||
<!-- #ifdef H5 -->
|
||||
<a class="ml-[10rpx] phone text-muted underline" :href="'tel:' + content.mobile">
|
||||
拨打
|
||||
</a>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 -->
|
||||
<view class="ml-[10rpx] phone text-muted underline" @click="handleCall">拨打</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="mt-[100rpx] w-full">
|
||||
<u-button type="primary" shape="circle" @click="saveImageToPhotosAlbum(content.qrcode)">
|
||||
保存二维码图片
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { saveImageToPhotosAlbum } from '@/utils/file'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const { getImageUrl } = useAppStore()
|
||||
|
||||
const handleCall = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: String(props.content.mobile)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<div class="my-service bg-white mx-[20rpx] mt-[20rpx] rounded-lg">
|
||||
<div
|
||||
v-if="content.title"
|
||||
class="title px-[30rpx] py-[20rpx] border-light border-solid border-0 border-b"
|
||||
>
|
||||
<div>{{ content.title }}</div>
|
||||
</div>
|
||||
<div v-if="content.style == 1" class="flex flex-wrap pt-[40rpx] pb-[20rpx]">
|
||||
<div
|
||||
v-for="(item, index) in content.data"
|
||||
:key="index"
|
||||
class="flex flex-col items-center w-1/4 mb-[15px]"
|
||||
>
|
||||
<u-image width="52" height="52" :src="getImageUrl(item.image)" alt="" />
|
||||
<div class="mt-[7px]">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="content.style == 2">
|
||||
<div
|
||||
v-for="(item, index) in content.data"
|
||||
:key="index"
|
||||
class="flex items-center border-light border-solid border-0 border-b h-[100rpx] px-[24rpx]"
|
||||
>
|
||||
<u-image width="48" height="48" :src="item.image" alt="" />
|
||||
<div class="ml-[20rpx] flex-1">{{ item.name }}</div>
|
||||
<div class="text-muted">
|
||||
<u-icon name="arrow-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<view class="nav pt-[30rpx] pb-[16rpx] bg-white">
|
||||
<view class="nav pt-[30rpx] pb-[16rpx] bg-white" v-if="content.data.length && content.enabled">
|
||||
<view class="nav-item flex flex-wrap">
|
||||
<view
|
||||
v-for="(item, index) in content.data"
|
||||
:key="index"
|
||||
class="flex flex-col items-center w-1/5 mb-[30rpx]"
|
||||
>
|
||||
<u-image width="41px" height="41px" :src="item.image" alt="" />
|
||||
<u-image width="41px" height="41px" :src="getImageUrl(item.image)" alt="" />
|
||||
<view class="mt-[14rpx]">{{ item.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -14,6 +14,8 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
|
|
@ -24,6 +26,8 @@ const props = defineProps({
|
|||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const { getImageUrl } = useAppStore()
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<view
|
||||
class="banner h-[200rpx] mx-[20rpx] mt-[20rpx] translate-y-0"
|
||||
v-if="content.data.length && content.enabled"
|
||||
>
|
||||
<swiper
|
||||
class="swiper h-full"
|
||||
:indicator-dots="content.data.length > 1"
|
||||
indicator-active-color="#4173ff"
|
||||
:autoplay="true"
|
||||
>
|
||||
<swiper-item v-for="(item, index) in content.data" :key="index">
|
||||
<u-image
|
||||
mode="aspectFit"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:src="getImageUrl(item.image)"
|
||||
:border-radius="14"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<div class="user-info flex items-center px-[50rpx]">
|
||||
<navigator
|
||||
v-if="isLogin"
|
||||
class="flex items-center"
|
||||
hover-class="none"
|
||||
url="/pages/login/login"
|
||||
>
|
||||
<u-avatar :src="user.avatar" :size="120"></u-avatar>
|
||||
<div class="text-white text-3xl ml-[20rpx]">{{ user.nickname }}</div>
|
||||
</navigator>
|
||||
<navigator v-else class="flex items-center" hover-class="none" url="/pages/login/login">
|
||||
<u-avatar src="/static/images/user/default_avatar.png" :size="120"></u-avatar>
|
||||
<div class="text-white text-3xl ml-[20rpx]">未登录</div>
|
||||
</navigator>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
user: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isLogin: {
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-info {
|
||||
background: url(/static/images/user/my_topbg.png);
|
||||
height: 115px;
|
||||
background-position: bottom;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { ref } from 'vue'
|
||||
|
||||
export function useLockFn(fn: (...args: any[]) => Promise<any>) {
|
||||
const isLock = ref(false)
|
||||
const lockFn = async (...args: any[]) => {
|
||||
if (isLock.value) return
|
||||
isLock.value = true
|
||||
try {
|
||||
const res = await fn(...args)
|
||||
isLock.value = false
|
||||
return res
|
||||
} catch (e) {
|
||||
isLock.value = false
|
||||
throw e
|
||||
}
|
||||
}
|
||||
return {
|
||||
isLock,
|
||||
lockFn
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
import { createSSRApp } from 'vue'
|
||||
// import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
import plugins from './plugins'
|
||||
import './styles/index.scss'
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
console.log(app.config.globalProperties)
|
||||
app.use(plugins)
|
||||
return {
|
||||
app
|
||||
|
|
|
|||
|
|
@ -29,7 +29,19 @@
|
|||
"style": {
|
||||
"navigationBarTitleText": "注册"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/customer_service/customer_service",
|
||||
"style": {
|
||||
"navigationBarTitleText": "联系客服"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/news_detail/news_detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "详情"
|
||||
}
|
||||
},
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
|
|
@ -38,20 +50,26 @@
|
|||
"backgroundColor": "#F8F8F8"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#7A7E83",
|
||||
"selectedColor": "#3cc51f",
|
||||
"color": "#666666",
|
||||
"selectedColor": "#4173FF",
|
||||
"borderStyle": "black",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"iconPath": "/static/images/tabs/home.png",
|
||||
"selectedIconPath": "/static/images/tabs/home_s.png",
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"iconPath": "/static/images/tabs/news.png",
|
||||
"selectedIconPath": "/static/images/tabs/news_s.png",
|
||||
"pagePath": "pages/news/news",
|
||||
"text": "资讯"
|
||||
},
|
||||
{
|
||||
"iconPath": "/static/images/tabs/user.png",
|
||||
"selectedIconPath": "/static/images/tabs/user_s.png",
|
||||
"pagePath": "pages/user/user",
|
||||
"text": "我的"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<view class="customer-service">
|
||||
<view v-for="(item, index) in state.pages" :key="index">
|
||||
<template v-if="item.name == 'customer-service'">
|
||||
<w-customer-service :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getDecorate } from '@/api/shop'
|
||||
import { reactive } from 'vue'
|
||||
const state = reactive<{
|
||||
pages: any[]
|
||||
}>({
|
||||
pages: []
|
||||
})
|
||||
const getData = async () => {
|
||||
const data = await getDecorate({ id: 3 })
|
||||
state.pages = JSON.parse(data.pages)
|
||||
}
|
||||
getData()
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
|
@ -1,14 +1,26 @@
|
|||
<template>
|
||||
<view class="index">
|
||||
<decoration :pages="state.pages" />
|
||||
<view v-for="(item, index) in state.pages" :key="index">
|
||||
<template v-if="item.name == 'search'">
|
||||
<w-search :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
<template v-if="item.name == 'banner'">
|
||||
<w-banner :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
<template v-if="item.name == 'nav'">
|
||||
<w-nav :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
</view>
|
||||
<view class="article"> </view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIndex } from '@/api/shop'
|
||||
import { reactive } from 'vue'
|
||||
const state = reactive({
|
||||
import { reactive, ref } from 'vue'
|
||||
const state = reactive<{
|
||||
pages: any[]
|
||||
}>({
|
||||
pages: []
|
||||
})
|
||||
const getData = async () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<u-form borderBottom>
|
||||
<template v-if="scene == LoginTypeEnum.ACCOUNT">
|
||||
<u-form-item borderBottom>
|
||||
<u-icon class="mr-2" :size="36" name="/static/images/icon_user.png" />
|
||||
<u-icon class="mr-2" :size="36" name="/static/images/icon/icon_user.png" />
|
||||
<u-input
|
||||
class="flex-1"
|
||||
v-model="formData.username"
|
||||
|
|
@ -19,7 +19,11 @@
|
|||
/>
|
||||
</u-form-item>
|
||||
<u-form-item borderBottom>
|
||||
<u-icon class="mr-2" :size="36" name="/static/images/icon_password.png" />
|
||||
<u-icon
|
||||
class="mr-2"
|
||||
:size="36"
|
||||
name="/static/images/icon/icon_password.png"
|
||||
/>
|
||||
<u-input
|
||||
class="flex-1"
|
||||
v-model="formData.password"
|
||||
|
|
@ -36,7 +40,11 @@
|
|||
</template>
|
||||
<template v-if="scene == LoginTypeEnum.MOBILE">
|
||||
<u-form-item borderBottom>
|
||||
<u-icon class="mr-2" :size="36" name="/static/images/icon_mobile.png" />
|
||||
<u-icon
|
||||
class="mr-2"
|
||||
:size="36"
|
||||
name="/static/images/icon/icon_mobile.png"
|
||||
/>
|
||||
<u-input
|
||||
class="flex-1"
|
||||
v-model="formData.mobile"
|
||||
|
|
@ -45,7 +53,7 @@
|
|||
/>
|
||||
</u-form-item>
|
||||
<u-form-item borderBottom>
|
||||
<u-icon class="mr-2" :size="36" name="/static/images/icon_code.png" />
|
||||
<u-icon class="mr-2" :size="36" name="/static/images/icon/icon_code.png" />
|
||||
<u-input
|
||||
class="flex-1"
|
||||
v-model="formData.code"
|
||||
|
|
@ -77,7 +85,7 @@
|
|||
</u-checkbox>
|
||||
</view>
|
||||
<view class="mt-[40rpx]">
|
||||
<u-button type="primary" shape="circle" @click="accountLogin(scene)">
|
||||
<u-button type="primary" shape="circle" @click="handleLogin(scene)">
|
||||
登 录
|
||||
</u-button>
|
||||
</view>
|
||||
|
|
@ -95,7 +103,7 @@
|
|||
<u-divider>第三方登录</u-divider>
|
||||
<div class="flex justify-center mt-[40rpx]">
|
||||
<div class="flex flex-col items-center" @click="wxLogin">
|
||||
<u-icon name="/static/images/icon_wx.png" size="80" />
|
||||
<u-icon name="/static/images/icon/icon_wx.png" size="80" />
|
||||
<div class="text-sm mt-[10px]">微信登录</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -108,7 +116,9 @@
|
|||
import { login } from '@/api/account'
|
||||
import { smsSend } from '@/api/app'
|
||||
import { SMSEnum } from '@/enums/appEnums'
|
||||
import { reactive, ref, shallowRef } from 'vue'
|
||||
import { useLockFn } from '@/hooks/useLockFn'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { reactive, ref, shallowRef, watch } from 'vue'
|
||||
enum LoginTypeEnum {
|
||||
MOBILE = 'mobile',
|
||||
ACCOUNT = 'account',
|
||||
|
|
@ -118,6 +128,8 @@ const uCodeRef = shallowRef()
|
|||
const scene = ref(LoginTypeEnum.ACCOUNT)
|
||||
const codeTips = ref('')
|
||||
const isCheckAgreement = ref(false)
|
||||
|
||||
const userStore = useUserStore()
|
||||
const formData = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
|
|
@ -140,7 +152,8 @@ const sendSms = async () => {
|
|||
uCodeRef.value?.start()
|
||||
}
|
||||
}
|
||||
const accountLogin = async (scene: LoginTypeEnum, code?: string) => {
|
||||
|
||||
const loginFun = async (scene: LoginTypeEnum, code?: string) => {
|
||||
if (!isCheckAgreement.value) return uni.$u.toast('请勾选已阅读并同意《服务协议》和《隐私协议》')
|
||||
if (scene == LoginTypeEnum.ACCOUNT) {
|
||||
if (!formData.username) return uni.$u.toast('请输入账号/手机号码')
|
||||
|
|
@ -155,17 +168,29 @@ const accountLogin = async (scene: LoginTypeEnum, code?: string) => {
|
|||
scene
|
||||
}
|
||||
if (code) params.code = code
|
||||
await login(params)
|
||||
uni.$u.toast('登录成功')
|
||||
uni.navigateBack()
|
||||
uni.showLoading({
|
||||
title: '请稍后...'
|
||||
})
|
||||
try {
|
||||
const data = await login(params)
|
||||
userStore.login(data.token)
|
||||
await userStore.getUser()
|
||||
uni.$u.toast('登录成功')
|
||||
uni.hideLoading()
|
||||
uni.navigateBack()
|
||||
} catch (error: any) {
|
||||
uni.hideLoading()
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const { isLock, lockFn: handleLogin } = useLockFn(loginFun)
|
||||
|
||||
const wxLogin = async () => {
|
||||
const data: any = await uni.login({
|
||||
provider: 'weixin'
|
||||
})
|
||||
console.log(data)
|
||||
accountLogin(LoginTypeEnum.MNP, data.code)
|
||||
handleLogin(LoginTypeEnum.MNP, data.code)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<z-paging ref="paging" v-model="dataList" v-if="i == index" @query="queryList" :fixed="false" height="100%"
|
||||
use-page-scroll>
|
||||
<block v-for="(newsItem,newsIndex) in dataList" :key="newsIndex">
|
||||
<news-card :item="newsItem"></news-card>
|
||||
</block>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { getArticleList } from "@/api/news"
|
||||
|
||||
const props = withDefaults(defineProps < {
|
||||
cid: number,
|
||||
i: number,
|
||||
index: number
|
||||
} > (), {
|
||||
cid: 0
|
||||
})
|
||||
|
||||
const paging = ref(null)
|
||||
const dataList = ref([{
|
||||
title: '123'
|
||||
}])
|
||||
|
||||
const queryList = async (pageNo, pageSize) => {
|
||||
// console.log(pageNo, pageSize)
|
||||
const {
|
||||
lists
|
||||
} = await getArticleList({
|
||||
cid: props.cid,
|
||||
pageNo,
|
||||
pageSize
|
||||
})
|
||||
console.log(lists)
|
||||
paging.value.complete(lists);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -1,7 +1,63 @@
|
|||
<template>
|
||||
<view class="content">资讯</view>
|
||||
</template>
|
||||
<view class="news" >
|
||||
<!-- 搜索 -->
|
||||
<view class="news-search px-[24rpx] py-[14rpx] bg-white">
|
||||
<u-search placeholder="请输入关键词搜索" disabled :show-action="false"></u-search>
|
||||
</view>
|
||||
|
||||
<!-- 内容 -->
|
||||
<tabs
|
||||
:current="current"
|
||||
@change="handleChange"
|
||||
height="80"
|
||||
bar-width="60"
|
||||
:barStyle="{'bottom': '0'}"
|
||||
>
|
||||
<tab
|
||||
v-for="(item, i) in tabList"
|
||||
:key="i"
|
||||
:name="item.name"
|
||||
>
|
||||
<view class="news-list pt-[20rpx]">
|
||||
<news-list :cid="item.id" :i="i" :index="current"></news-list>
|
||||
</view>
|
||||
</tab>
|
||||
</tabs>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed } from "vue"
|
||||
import { onLoad, onShow, onReady } from "@dcloudio/uni-app";
|
||||
import NewsList from "./component/news-list.vue"
|
||||
import { getArticleCate } from "@/api/news"
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style></style>
|
||||
|
||||
const tabList = ref< any >([])
|
||||
const current = ref<number>(0)
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
console.log(index)
|
||||
current.value = Number(index)
|
||||
}
|
||||
|
||||
const getData = async () => {
|
||||
tabList.value = await getArticleCate()
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
getData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.news {
|
||||
&-search {
|
||||
margin-bottom: 2rpx;
|
||||
}
|
||||
|
||||
&-list {
|
||||
height: calc(100vh - 86px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
<template>
|
||||
<view class="register min-h-full flex flex-col items-center px-[40rpx] pt-[40rpx] box-border">
|
||||
<view
|
||||
class="register bg-white min-h-full flex flex-col items-center px-[40rpx] pt-[40rpx] box-border"
|
||||
>
|
||||
<view class="w-full">
|
||||
<u-form borderBottom :label-width="150">
|
||||
<u-form-item label="创建账号" borderBottom>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,45 @@
|
|||
<template>
|
||||
<view class="content">个人设置</view>
|
||||
<view class="user">
|
||||
<view v-for="(item, index) in state.pages" :key="index">
|
||||
<template v-if="item.name == 'user-info'">
|
||||
<w-user-info
|
||||
:content="item.content"
|
||||
:styles="item.styles"
|
||||
:user="userInfo"
|
||||
:is-login="isLogin"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="item.name == 'my-service'">
|
||||
<w-my-service :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
<template v-if="item.name == 'user-banner'">
|
||||
<w-user-banner :content="item.content" :styles="item.styles" />
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { getDecorate } from '@/api/shop'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { reactive } from 'vue'
|
||||
const state = reactive<{
|
||||
pages: any[]
|
||||
}>({
|
||||
pages: []
|
||||
})
|
||||
const getData = async () => {
|
||||
const data = await getDecorate({ id: 2 })
|
||||
state.pages = JSON.parse(data.pages)
|
||||
}
|
||||
const userStore = useUserStore()
|
||||
const { userInfo, isLogin } = storeToRefs(userStore)
|
||||
onShow(() => {
|
||||
userStore.getUser()
|
||||
})
|
||||
getData()
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 679 B After Width: | Height: | Size: 679 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 831 B |
|
After Width: | Height: | Size: 839 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -1,8 +1,22 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { getConfig } from '@/api/app'
|
||||
|
||||
interface AppSate {
|
||||
config: Record<string, any>
|
||||
}
|
||||
export const useAppStore = defineStore({
|
||||
id: 'userStore',
|
||||
state: () => ({}),
|
||||
id: 'appStore',
|
||||
state: (): AppSate => ({
|
||||
config: {}
|
||||
}),
|
||||
getters: {},
|
||||
actions: {}
|
||||
actions: {
|
||||
getImageUrl(url: string) {
|
||||
return url ? `${this.config.domain}${url}` : ''
|
||||
},
|
||||
async getConfig() {
|
||||
const data = await getConfig()
|
||||
this.config = data
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,12 +1,34 @@
|
|||
import { getClient } from '@/utils/client'
|
||||
import { getUserCenter } from '@/api/user'
|
||||
import { TOKEN_KEY } from '@/enums/cacheEnums'
|
||||
import cache from '@/utils/cache'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useAppStore = defineStore({
|
||||
id: 'appStore',
|
||||
// convert to a function
|
||||
state: () => ({
|
||||
client: getClient()
|
||||
interface UserSate {
|
||||
userInfo: Record<string, any>
|
||||
token: string | null
|
||||
}
|
||||
export const useUserStore = defineStore({
|
||||
id: 'userStore',
|
||||
state: (): UserSate => ({
|
||||
userInfo: {},
|
||||
token: cache.get(TOKEN_KEY) || null
|
||||
}),
|
||||
getters: {},
|
||||
actions: {}
|
||||
getters: {
|
||||
isLogin: (state) => !!state.token
|
||||
},
|
||||
actions: {
|
||||
async getUser() {
|
||||
const data = await getUserCenter()
|
||||
this.userInfo = data
|
||||
},
|
||||
login(token: string) {
|
||||
this.token = token
|
||||
cache.set(TOKEN_KEY, token)
|
||||
},
|
||||
logout() {
|
||||
this.token = ''
|
||||
this.userInfo = {}
|
||||
cache.remove(TOKEN_KEY)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const cache = {
|
|||
data = JSON.stringify(data)
|
||||
}
|
||||
try {
|
||||
window.localStorage.setItem(key, data)
|
||||
uni.setStorageSync(key, data)
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
|
|
@ -20,13 +20,13 @@ const cache = {
|
|||
get(key: string) {
|
||||
key = this.getKey(key)
|
||||
try {
|
||||
const data = window.localStorage.getItem(key)
|
||||
const data = uni.getStorageSync(key)
|
||||
if (!data) {
|
||||
return null
|
||||
}
|
||||
const { value, expire } = JSON.parse(data)
|
||||
if (expire && expire < this.time()) {
|
||||
window.localStorage.removeItem(key)
|
||||
uni.removeStorageSync(key)
|
||||
return null
|
||||
}
|
||||
return value
|
||||
|
|
@ -40,7 +40,7 @@ const cache = {
|
|||
},
|
||||
remove(key: string) {
|
||||
key = this.getKey(key)
|
||||
window.localStorage.removeItem(key)
|
||||
uni.removeStorageSync(key)
|
||||
},
|
||||
getKey(key: string) {
|
||||
return this.key + key
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
export async function saveImageToPhotosAlbum(url: string) {
|
||||
if (!url) return uni.$u.$toast('图片不存在')
|
||||
//#ifdef H5
|
||||
uni.$u.$toast('长按图片保存')
|
||||
//#endif
|
||||
try {
|
||||
const res: any = await uni.downloadFile({ url, timeout: 10000 })
|
||||
await uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath
|
||||
})
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
})
|
||||
} catch (error: any) {
|
||||
uni.showToast({
|
||||
title: error.errMsg || '保存失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||