Merge remote-tracking branch 'origin/dev' into dev
commit
57474c0644
@ -0,0 +1,43 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface ProductVO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
no: string
|
||||||
|
unit: string
|
||||||
|
price: number
|
||||||
|
status: number
|
||||||
|
categoryId: number
|
||||||
|
description: string
|
||||||
|
ownerUserId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询产品列表
|
||||||
|
export const getProductPage = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/product/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询产品详情
|
||||||
|
export const getProduct = async (id: number) => {
|
||||||
|
return await request.get({ url: `/crm/product/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增产品
|
||||||
|
export const createProduct = async (data: ProductVO) => {
|
||||||
|
return await request.post({ url: `/crm/product/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改产品
|
||||||
|
export const updateProduct = async (data: ProductVO) => {
|
||||||
|
return await request.put({ url: `/crm/product/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除产品
|
||||||
|
export const deleteProduct = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/crm/product/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出产品 Excel
|
||||||
|
export const exportProduct = async (params) => {
|
||||||
|
return await request.download({ url: `/crm/product/export-excel`, params })
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface SocialClientVO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
socialType: number
|
||||||
|
userType: number
|
||||||
|
clientId: string
|
||||||
|
clientSecret: string
|
||||||
|
agentId: string
|
||||||
|
status: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询社交客户端列表
|
||||||
|
export const getSocialClientPage = async (params) => {
|
||||||
|
return await request.get({ url: `/system/social-client/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询社交客户端详情
|
||||||
|
export const getSocialClient = async (id: number) => {
|
||||||
|
return await request.get({ url: `/system/social-client/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增社交客户端
|
||||||
|
export const createSocialClient = async (data: SocialClientVO) => {
|
||||||
|
return await request.post({ url: `/system/social-client/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改社交客户端
|
||||||
|
export const updateSocialClient = async (data: SocialClientVO) => {
|
||||||
|
return await request.put({ url: `/system/social-client/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除社交客户端
|
||||||
|
export const deleteSocialClient = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/system/social-client/delete?id=` + id })
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface SocialUserVO {
|
||||||
|
id: number
|
||||||
|
type: number
|
||||||
|
openid: string
|
||||||
|
token: string
|
||||||
|
rawTokenInfo: string
|
||||||
|
nickname: string
|
||||||
|
avatar: string
|
||||||
|
rawUserInfo: string
|
||||||
|
code: string
|
||||||
|
state: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询社交用户列表
|
||||||
|
export const getSocialUserPage = async (params) => {
|
||||||
|
return await request.get({ url: `/system/social-user/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询社交用户详情
|
||||||
|
export const getSocialUser = async (id: number) => {
|
||||||
|
return await request.get({ url: `/system/social-user/get?id=` + id })
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 图片展示属性 */
|
||||||
|
export interface ImageBarProperty {
|
||||||
|
// 图片链接
|
||||||
|
imgUrl: string
|
||||||
|
// 跳转链接
|
||||||
|
url: string
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'ImageBar',
|
||||||
|
name: '图片展示',
|
||||||
|
icon: 'ep:picture',
|
||||||
|
property: {
|
||||||
|
imgUrl: '',
|
||||||
|
url: '',
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '#fff',
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<ImageBarProperty>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 无图片 -->
|
||||||
|
<div class="h-50px flex items-center justify-center bg-gray-3" v-if="!property.imgUrl">
|
||||||
|
<Icon icon="ep:picture" class="text-gray-8 text-30px!" />
|
||||||
|
</div>
|
||||||
|
<el-image class="min-h-30px" v-else :src="property.imgUrl" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ImageBarProperty } from './config'
|
||||||
|
|
||||||
|
/** 图片展示 */
|
||||||
|
defineOptions({ name: 'ImageBar' })
|
||||||
|
|
||||||
|
defineProps<{ property: ImageBarProperty }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
/* 图片 */
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<el-form label-width="80px" :model="formData">
|
||||||
|
<el-form-item label="上传图片" prop="imgUrl">
|
||||||
|
<UploadImg
|
||||||
|
v-model="formData.imgUrl"
|
||||||
|
draggable="false"
|
||||||
|
height="80px"
|
||||||
|
width="100%"
|
||||||
|
class="min-w-80px"
|
||||||
|
>
|
||||||
|
<template #tip> 建议宽度750 </template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="链接" prop="url">
|
||||||
|
<el-input placeholder="链接" v-model="formData.url" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ImageBarProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 图片展示属性面板
|
||||||
|
defineOptions({ name: 'ImageBarProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: ImageBarProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 视频播放属性 */
|
||||||
|
export interface VideoPlayerProperty {
|
||||||
|
// 视频链接
|
||||||
|
videoUrl: string
|
||||||
|
// 封面链接
|
||||||
|
posterUrl: string
|
||||||
|
// 是否自动播放
|
||||||
|
autoplay: boolean
|
||||||
|
// 组件样式
|
||||||
|
style: VideoPlayerStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频播放样式
|
||||||
|
export interface VideoPlayerStyle extends ComponentStyle {
|
||||||
|
// 视频高度
|
||||||
|
height: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'VideoPlayer',
|
||||||
|
name: '视频播放',
|
||||||
|
icon: 'ep:video-play',
|
||||||
|
property: {
|
||||||
|
videoUrl: '',
|
||||||
|
posterUrl: '',
|
||||||
|
autoplay: false,
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '#fff',
|
||||||
|
marginBottom: 8,
|
||||||
|
height: 300
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<VideoPlayerProperty>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full" :style="{ height: `${property.style.height}px` }">
|
||||||
|
<el-image class="w-full w-full" :src="property.posterUrl" v-if="property.posterUrl" />
|
||||||
|
<video
|
||||||
|
v-else
|
||||||
|
class="w-full w-full"
|
||||||
|
:src="property.videoUrl"
|
||||||
|
:poster="property.posterUrl"
|
||||||
|
:autoplay="property.autoplay"
|
||||||
|
controls
|
||||||
|
></video>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { VideoPlayerProperty } from './config'
|
||||||
|
|
||||||
|
/** 视频播放 */
|
||||||
|
defineOptions({ name: 'VideoPlayer' })
|
||||||
|
|
||||||
|
defineProps<{ property: VideoPlayerProperty }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
/* 图片 */
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<template #style="{ formData }">
|
||||||
|
<el-form-item label="高度" prop="height">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.height"
|
||||||
|
:max="500"
|
||||||
|
:min="100"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<el-form label-width="80px" :model="formData">
|
||||||
|
<el-form-item label="上传视频" prop="videoUrl">
|
||||||
|
<UploadFile
|
||||||
|
v-model="formData.videoUrl"
|
||||||
|
:file-type="['mp4']"
|
||||||
|
:limit="1"
|
||||||
|
:file-size="100"
|
||||||
|
class="min-w-80px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上传封面" prop="posterUrl">
|
||||||
|
<UploadImg
|
||||||
|
v-model="formData.posterUrl"
|
||||||
|
draggable="false"
|
||||||
|
height="80px"
|
||||||
|
width="100%"
|
||||||
|
class="min-w-80px"
|
||||||
|
>
|
||||||
|
<template #tip> 建议宽度750 </template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="自动播放" prop="autoplay">
|
||||||
|
<el-switch v-model="formData.autoplay" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { VideoPlayerProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 视频播放属性面板
|
||||||
|
defineOptions({ name: 'VideoPlayerProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: VideoPlayerProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { Config, driver } from 'driver.js'
|
||||||
|
import 'driver.js/dist/driver.css'
|
||||||
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const { variables } = useDesign()
|
||||||
|
|
||||||
|
export const useGuide = (options?: Config) => {
|
||||||
|
const driverObj = driver(
|
||||||
|
options || {
|
||||||
|
showProgress: true,
|
||||||
|
nextBtnText: t('common.nextLabel'),
|
||||||
|
prevBtnText: t('common.prevLabel'),
|
||||||
|
doneBtnText: t('common.doneLabel'),
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
element: `#${variables.namespace}-menu`,
|
||||||
|
popover: {
|
||||||
|
title: t('common.menu'),
|
||||||
|
description: t('common.menuDes'),
|
||||||
|
side: 'right'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: `#${variables.namespace}-tool-header`,
|
||||||
|
popover: {
|
||||||
|
title: t('common.tool'),
|
||||||
|
description: t('common.toolDes'),
|
||||||
|
side: 'left'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: `#${variables.namespace}-tags-view`,
|
||||||
|
popover: {
|
||||||
|
title: t('common.tagsView'),
|
||||||
|
description: t('common.tagsViewDes'),
|
||||||
|
side: 'bottom'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...driverObj
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,47 +0,0 @@
|
|||||||
import introJs from 'intro.js'
|
|
||||||
import { IntroJs, Step, Options } from 'intro.js'
|
|
||||||
import 'intro.js/introjs.css'
|
|
||||||
|
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
|
||||||
|
|
||||||
export const useIntro = (setps?: Step[], options?: Options) => {
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const { variables } = useDesign()
|
|
||||||
|
|
||||||
const defaultSetps: Step[] = setps || [
|
|
||||||
{
|
|
||||||
element: `#${variables.namespace}-menu`,
|
|
||||||
title: t('common.menu'),
|
|
||||||
intro: t('common.menuDes'),
|
|
||||||
position: 'right'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: `#${variables.namespace}-tool-header`,
|
|
||||||
title: t('common.tool'),
|
|
||||||
intro: t('common.toolDes'),
|
|
||||||
position: 'left'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: `#${variables.namespace}-tags-view`,
|
|
||||||
title: t('common.tagsView'),
|
|
||||||
intro: t('common.tagsViewDes'),
|
|
||||||
position: 'bottom'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const defaultOptions: Options = options || {
|
|
||||||
prevLabel: t('common.prevLabel'),
|
|
||||||
nextLabel: t('common.nextLabel'),
|
|
||||||
skipLabel: t('common.skipLabel'),
|
|
||||||
doneLabel: t('common.doneLabel')
|
|
||||||
}
|
|
||||||
|
|
||||||
const introRef: IntroJs = introJs()
|
|
||||||
|
|
||||||
introRef.addSteps(defaultSetps).setOptions(defaultOptions)
|
|
||||||
|
|
||||||
return {
|
|
||||||
introRef
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { ref, onBeforeUnmount } from 'vue'
|
||||||
|
|
||||||
|
const useNetwork = () => {
|
||||||
|
const online = ref(true)
|
||||||
|
|
||||||
|
const updateNetwork = () => {
|
||||||
|
online.value = navigator.onLine
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('online', updateNetwork)
|
||||||
|
window.addEventListener('offline', updateNetwork)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('online', updateNetwork)
|
||||||
|
window.removeEventListener('offline', updateNetwork)
|
||||||
|
})
|
||||||
|
|
||||||
|
return { online }
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useNetwork }
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
import { dateUtil } from '@/utils/dateUtil'
|
||||||
|
import { reactive, toRefs } from 'vue'
|
||||||
|
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core'
|
||||||
|
|
||||||
|
export const useNow = (immediate = true) => {
|
||||||
|
let timer: IntervalHandle
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
year: 0,
|
||||||
|
month: 0,
|
||||||
|
week: '',
|
||||||
|
day: 0,
|
||||||
|
hour: '',
|
||||||
|
minute: '',
|
||||||
|
second: 0,
|
||||||
|
meridiem: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
const now = dateUtil()
|
||||||
|
|
||||||
|
const h = now.format('HH')
|
||||||
|
const m = now.format('mm')
|
||||||
|
const s = now.get('s')
|
||||||
|
|
||||||
|
state.year = now.get('y')
|
||||||
|
state.month = now.get('M') + 1
|
||||||
|
state.week = '星期' + ['日', '一', '二', '三', '四', '五', '六'][now.day()]
|
||||||
|
state.day = now.get('date')
|
||||||
|
state.hour = h
|
||||||
|
state.minute = m
|
||||||
|
state.second = s
|
||||||
|
|
||||||
|
state.meridiem = now.format('A')
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
update()
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = setInterval(() => update(), 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
|
||||||
|
tryOnMounted(() => {
|
||||||
|
immediate && start()
|
||||||
|
})
|
||||||
|
|
||||||
|
tryOnUnmounted(() => {
|
||||||
|
stop()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
start,
|
||||||
|
stop
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { useTagsViewStoreWithOut } from '@/store/modules/tagsView'
|
||||||
|
import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router'
|
||||||
|
import { computed, nextTick, unref } from 'vue'
|
||||||
|
|
||||||
|
export const useTagsView = () => {
|
||||||
|
const tagsViewStore = useTagsViewStoreWithOut()
|
||||||
|
|
||||||
|
const { replace, currentRoute } = useRouter()
|
||||||
|
|
||||||
|
const selectedTag = computed(() => tagsViewStore.getSelectedTag)
|
||||||
|
|
||||||
|
const closeAll = (callback?: Fn) => {
|
||||||
|
tagsViewStore.delAllViews()
|
||||||
|
callback?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeLeft = (callback?: Fn) => {
|
||||||
|
tagsViewStore.delLeftViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
||||||
|
callback?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeRight = (callback?: Fn) => {
|
||||||
|
tagsViewStore.delRightViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
||||||
|
callback?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeOther = (callback?: Fn) => {
|
||||||
|
tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
||||||
|
callback?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeCurrent = (view?: RouteLocationNormalizedLoaded, callback?: Fn) => {
|
||||||
|
if (view?.meta?.affix) return
|
||||||
|
tagsViewStore.delView(view || unref(currentRoute))
|
||||||
|
|
||||||
|
callback?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshPage = async (view?: RouteLocationNormalizedLoaded, callback?: Fn) => {
|
||||||
|
tagsViewStore.delCachedView()
|
||||||
|
const { path, query } = view || unref(currentRoute)
|
||||||
|
await nextTick()
|
||||||
|
replace({
|
||||||
|
path: '/redirect' + path,
|
||||||
|
query: query
|
||||||
|
})
|
||||||
|
callback?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const setTitle = (title: string, path?: string) => {
|
||||||
|
tagsViewStore.setTitle(title, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
closeAll,
|
||||||
|
closeLeft,
|
||||||
|
closeRight,
|
||||||
|
closeOther,
|
||||||
|
closeCurrent,
|
||||||
|
refreshPage,
|
||||||
|
setTitle
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue