初版本更新

master
飘泊客 10 months ago
parent d31cfd561c
commit 22da3e9c8d

@ -0,0 +1,16 @@
{ // launch.json configurations app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtypelocalremote, localremote
"version": "0.0",
"configurations": [{
"app-plus" :
{
"launchtype" : "local"
},
"default" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

19
components.d.ts vendored

@ -7,40 +7,27 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
AccountLoginForm: typeof import('./src/components/login/AccountLoginForm.vue')['default']
AssetsList: typeof import('./src/components/home/AssetsList.vue')['default']
ConcatCaptcha: typeof import('./src/components/Captcha/ConcatCaptcha.vue')['default']
CpIcon: typeof import('./src/components/CpIcon.vue')['default']
DoctorCard: typeof import('./src/components/home/DoctorCard.vue')['default']
FollowDoctor: typeof import('./src/components/home/FollowDoctor.vue')['default']
KnowledgeCard: typeof import('./src/components/home/KnowledgeCard.vue')['default']
KnowledgeList: typeof import('./src/components/home/KnowledgeList.vue')['default']
RotateCaptcha: typeof import('./src/components/Captcha/RotateCaptcha.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SliderCaptcha: typeof import('./src/components/Captcha/SliderCaptcha.vue')['default']
SssetsList: typeof import('./src/components/home/SssetsList.vue')['default']
VanActionSheet: typeof import('vant/es')['ActionSheet']
VanButton: typeof import('vant/es')['Button']
VanCalendar: typeof import('vant/es')['Calendar']
VanCell: typeof import('vant/es')['Cell']
VanCellGroup: typeof import('vant/es')['CellGroup']
VanCol: typeof import('vant/es')['Col']
VanDatePicker: typeof import('vant/es')['DatePicker']
VanField: typeof import('vant/es')['Field']
VanForm: typeof import('vant/es')['Form']
VanIcon: typeof import('vant/es')['Icon']
VanImage: typeof import('vant/es')['Image']
VanList: typeof import('vant/es')['List']
VanNavBar: typeof import('vant/es')['NavBar']
VanPicker: typeof import('vant/es')['Picker']
VanPopup: typeof import('vant/es')['Popup']
VanRow: typeof import('vant/es')['Row']
VanSearch: typeof import('vant/es')['Search']
VanStep: typeof import('vant/es')['Step']
VanSteps: typeof import('vant/es')['Steps']
VanSticky: typeof import('vant/es')['Sticky']
VanSwipe: typeof import('vant/es')['Swipe']
VanSwipeItem: typeof import('vant/es')['SwipeItem']
VanTab: typeof import('vant/es')['Tab']
VanTabs: typeof import('vant/es')['Tabs']
VanTag: typeof import('vant/es')['Tag']
WordClickCaptcha: typeof import('./src/components/Captcha/WordClickCaptcha.vue')['default']
}

11220
package-lock.json generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -13,7 +13,8 @@
import {
getToken
} from "@/utils/Storage";
import socket from "@/utils/socketTask";
import ws from "@/utils/socketTask";
const checkLogin = () => {
if (!getToken) {
Navigate.redirect('/pages/login')
@ -30,7 +31,14 @@
console.log('App Launch')
})
onShow(() => {
console.log('App Show')
if (getToken()) {
setTimeout(() => {
socket.initWebSocketTask()
}, 1000)
}
// #ifdef APP-PLUS
// #endif
})
onHide(() => {
console.log('App Hide')

@ -24,16 +24,6 @@ export interface CaptchaData {
}
}
}
/**
*
* @param type
* @returns
*/
export function captchaGen(type?: string) {
return request.get<CaptchaData>('/captcha/tianai/gen', { params: { type: type } })
}
/**
*
* @param id id

@ -1,16 +0,0 @@
//在线问诊
import type {
KnowledgePage,
KnowledgeParams,
PageParams,
DoctorPage
} from '@/types/consult'
import request from '@/utils/request'
// 获取健康知识列表
export const getKnowledgePage = (params: KnowledgeParams) =>
request.get<KnowledgePage>('/patient/home/knowledge', { params })
// 获取关注医生的列表
export const getDoctorPage = (params: PageParams) =>
request.get<DoctorPage>('/home/page/doc', { params })

@ -0,0 +1,21 @@
import type {
HomeSearchPage,
PageParams,
editClue,
AssetsPage
} from '@/types/consult'
import request from '@/utils/request'
// 资源列表
export const getKnowledgePage = (params: HomeSearchPage) =>
request.get<AssetsPage>('/clue/page', { params })
// 资源详情-id
export const getDetails = (id: number) =>
request.get<any>('/clue/details/' + id )
// 资源编辑
export const editClueUpdate = (data: editClue) =>
request.post<any>('/clue/update', data)
export const getReadCount = () =>
request.get<any>('/notify/user-announcement/not/read/count')
// export const getDoctorPage = (params: PageParams) =>
// request.get<DoctorPage>('/home/page/doc', { params })

@ -8,7 +8,7 @@
*/
import request from '@/utils/request'
import type { OAuth2LoginParam } from '@/components/Captcha/types'
import { LoginResult } from '@/types/user'
import { LoginResult, LoginObj } from '@/types/user'
const BASIC_AUTHORIZATION = 'Basic dWk6dWk='
export const loginAPI = (mobile: string, password: string) => {
return request.post('login/password', {
@ -29,4 +29,12 @@ export function accountLogin(parameter: OAuth2LoginParam) {
},
params: parameter
})
}
}
/**
*
* @returns
*/
export function getCodeData() {
return request.get<LoginObj>('/captcha/code')
}

@ -35,7 +35,8 @@ export interface AccountLoginParam {
username: string
password: string
grant_type: string
captchaId?: string
uuid?: string
code: number | null
}
/**

@ -6,109 +6,106 @@ import {
ref
} from 'vue'
import type {
AssetsList
AssetsList,
HomeSearchPage
} from '@/types/consult'
import {
getKnowledgePage
} from '@/api/consult'
interface SearchObj {
searchVal: string | number
} from '@/api/home'
interface SearchObj {
nid?: string
searchDate?: string
}
const props = defineProps<SearchObj>()
// const { nid, searchDate } = toRefs(props)
const emits = defineEmits(['clickDetail', 'toPlay'])
//
const list = ref<AssetsList>([
{ id: 1, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
{ id: 2, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
{ id: 3, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
{ id: 4, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
{ id: 5, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
{ id: 5, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
{ id: 5, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
{ id: 5, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
{ id: 5, name: '张三大大大大大大大大大大大大大大大大大大大大大大大大大大', state: 1, lastTime: 1690956863121, call: '18516647464', tag: ['人美', '温柔乡', '粘人', '毒蛇', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨', '话痨',] },
]);
const list = ref<AssetsList>([])
//:true false()
const loading = ref(false);
//:falsw true
const finished = ref(false);
//
let params = {
current: 1,
pageSize: 10,
searchVal: ''
let params: HomeSearchPage = {
page: 1,
size: 10,
nid: null,
startTime: null,
endTime: null
}
// onMounted(() => {
// console.log('22222222222222222222')
// })
//
const onLoad = async () => {
//
// setTimeout ajax
if (props.searchVal) {
params.searchVal = props.searchVal.toString()
}
const {
data
} = await getKnowledgePage(params)
if (params.searchVal) {
onMounted(() => {
initListData()
})
const initListData = async(type?: string) => {
if (type == 'search') {
initList()
}
//list
list.value.push(...data.rows)
//
if (props.nid) {
params.nid = props.nid
}
if (props.searchDate) {
let arr = props.searchDate.split('-')
params.endTime = arr[0]
params.startTime = arr[1]
}
const { data } = await getKnowledgePage(params)
list.value.push(...data.records)
loading.value = false;
//
// ?
//1list===total
//2current===pageTotal
if (list.value.length === data.total) {
//
finished.value = true;
} else {
//?+1
params.current++
params.page++
}
};
const clickDetail = (id: string) => {
uni.stopPullDownRefresh()
}
const clickDetail = (id: number) => {
emits('clickDetail', id)
}
const playCall = async (call: string) => {
emits('toPlay', call)
}
const initList = () => {
params.searchVal = ''
list.value = []
finished.value = false
params.current = 1
params.page = 1
params.nid = null
params.startTime = null
params.endTime = null
}
defineExpose({
initListData
})
// //
onPullDownRefresh(() => {
initListData('search')
})
</script>
<template>
<view class="knowledge-list">
<van-list v-model:loading="loading" :finished="finished" finished-text="" @load="onLoad">
<van-list v-model:loading="loading" :finished="finished" finished-text="" @load="initListData">
<!-- 列表数据 -->
<view class="mt-20rpx">
<view v-for="(item, index) in list" class="card-item bg-white p-15rpx mb-16rpx border-l-7 border-cyan-500" @click="clickDetail(item.id)">
<view v-for="item in list" :key="item.clueId" class="card-item bg-white p-15rpx mb-16rpx border-l-7 border-cyan-500 relative" @click="clickDetail(item.clueId)">
<view class="flex items-center justify-between">
<view class="font-bold text-c5 text-sm van-ellipsis flex-initial pr-15rpx">{{ item.name }}</view>
<view class="flex-none"><uni-icons type="smallcircle-filled" :class="item.state == 0 ? '' : 'text-c7'" size="10rpx"></uni-icons>{{ item.state == 0 ? '' : '' }}</view>
<view class="font-bold text-c5 text-sm van-ellipsis flex-initial pr-15rpx">{{ item.nid }}</view>
<view class="flex-none">{{ item.clueStageName ? item.clueStageName: '--' }}</view>
</view>
<view class="flex py-15rpx justify-between">
<view class="flex-initial van-ellipsis">
<text class="block text-xs mb-10rpx">最后跟进{{ parseTime(item.lastTime) }}</text>
<text class="block text-xs mb-10rpx">{{ item.originName }}</text>
<text class="block text-xs mb-10rpx">线索日期{{ item.clueTime ? item.clueTime : '--' }}</text>
<!-- <text class="block text-xs">标签有意向咨询多次</text> -->
<view class="van-ellipsis">
<van-tag v-for="it in item.tag" type="primary" class="mr-5rpx">{{ it }}</van-tag>
<van-tag v-for="it in item.clueLabelList" type="primary" class="mr-5rpx">{{ it }}</van-tag>
</view>
</view>
<view class="flex-none" @click.stop="playCall(item.call)">
<view class="flex-none" @click.stop="playCall(item.nid)">
<uni-icons custom-prefix="iconfont" class="text-c6! block mt-6rpx active:text-c7!" type="icon-dianhua" size="36"></uni-icons>
</view>
</view>
<img v-if="item.isNewClue" class="absolute w-44rpx top-0 left-0" src="@/static/images/new.png">
</view>
</view>
</van-list>

@ -13,5 +13,5 @@ export enum ENUM_STORAGE_KEY {
refreshToken = '__REFRESH_TOKEN__',
/** 用户信息 */
userInfo = '__USER_INFO__',
vitePasswordKey = '==BallCat-Auth=='
vitePasswordKey = '==ad-distribute='
}

@ -5,7 +5,6 @@
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
@ -16,11 +15,8 @@
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
@ -41,15 +37,11 @@
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {

@ -17,7 +17,8 @@
"path": "pages/common/assetsDetail/index",
"style": {
"navigationBarTitleText": "线索详情",
"enablePullDownRefresh": true
"enablePullDownRefresh": true,
"navigationStyle": "custom"
}
},
{
@ -80,12 +81,14 @@
"iconPath": "static/images/tabbar/work.png",
"selectedIconPath": "static/images/tabbar/work_.png",
"text": "客户列表"
}, {
"pagePath": "pages/assets/index",
"iconPath": "static/images/tabbar/home.png",
"selectedIconPath": "static/images/tabbar/home.png",
"text": "个人绩效"
}, {
},
// {
// "pagePath": "pages/assets/index",
// "iconPath": "static/images/tabbar/home.png",
// "selectedIconPath": "static/images/tabbar/home.png",
// "text": "个人绩效"
// },
{
"pagePath": "pages/mine/index",
"iconPath": "static/images/tabbar/mine.png",
"selectedIconPath": "static/images/tabbar/mine_.png",

@ -2,14 +2,32 @@
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { onLoad } from "@dcloudio/uni-app";
import { getDetails, editClueUpdate } from "@/api/home";
import { showToast } from 'vant';
let addProp = ref(false)
let editCode = ref(false)
let showcluePicker = ref(false)
const refForm = ref()
let addForm = reactive({
stageName: '',
record: [{val: ''}]
})
onLoad((option) => {
console.log("🚀 ~ file: index.vue:5 ~ onLoad ~ option:", option)
let customFieldName = {
text: 'name',
value: 'clueStageId'
}
let formData: any = ref({})
onLoad((option: any) => {
getDetails(option.id).then(res => {
formData.value = res.data
}).catch(err => {
showToast('数据错误')
setTimeout(() => {
uni.navigateBack({
delta: 1
})
}, 700)
})
})
const cancellationFun = () => {
addProp.value = false
@ -17,8 +35,60 @@ const cancellationFun = () => {
addForm.record = [{val: ''}]
refForm.value.resetValidation()
}
interface activeObj {
id: number
labelName: string
}
const activeArr = ref<activeObj[]>([])
const saveCode = async () => {
editCode.value = !editCode.value
if (editCode.value == true && formData.value.clueLabelName) {
JSON.parse(formData.value.clueLabelName).forEach((t1: any) => {
formData.value.organizeEntities.forEach((t2: any) => {
let data = t2.labelEntityList.filter((t3: any) => t3.labelName == t1)[0]
if (data) {
activeArr.value.push({id: data.id, labelName: data.labelName})
}
})
});
} else if(editCode.value == false && activeArr.value.length > 0) {
const res = await editClueUpdate({clueId: formData.value.clueId, clueLabelList: activeArr.value})
formData.value.clueLabelName = JSON.stringify(activeArr.value.map((item: any) => {
return item[Object.keys(item)[1]]
}))
activeArr.value = []
}
}
const addClueCode = (id: number, labelName: string) => {
let index = activeArr.value.findIndex((item: activeObj) => item.id == id)
if (index !== -1) {
activeArr.value.splice(index, 1)
} else {
activeArr.value.push({ id, labelName})
}
}
const onConfirm = (data: any) => {
// console.log("🚀 ~ file: index.vue:64 ~ onConfirm ~ res:", data.selectedOptions[0])
showcluePicker.value = false
editClueUpdate({clueId: formData.value.clueId, clueStageName: data.selectedOptions[0].name, clueStageId: data.selectedOptions[0].clueStageId}).then(res => {
formData.value.clueStageName = data.selectedOptions[0].name
uni.showToast({
title: '操作成功',
icon: 'none'
})
})
}
const compareId = (id: number) => {
return activeArr.value.some(item => item.id == id)
}
const onRecordSubmit = () => {
}
const onBack = () => {
uni.switchTab({
url: '/pages/index'
})
}
const addRecord = () => {
addForm.record.push({val: ''})
@ -28,15 +98,63 @@ const addRecord = () => {
<template>
<view>
<h2 class="title-doc-block text-base font-bold! py-20rpx px-32rpx text-c2 border-b-1 border-b-stone-300"><van-icon name="fire-o" color="#007aff" class="mr-10rpx"/>标签编辑</h2>
<view v-for="item in 2">
<h2 class="title-doc-block mt-32rpx py-24rpx px-32rpx fs-14">标签组1<text class="text-zinc-500">(单选)</text></h2>
<view class="px-32rpx flex flex-wrap">
<view v-for="it in 12" class="block-button mr-16rpx mb-16rpx" :class="{'tag-active': it == 3}">1</view>
</view>
<van-nav-bar
title="详情"
left-text="返回"
left-arrow
@click-left="onBack"
/>
<view class="flex justify-between border-b-1 border-b-stone-300 items-center px-32rpx py-20rpx bg-teal-500">
<h2 class="title-doc-block text-base font-bold! border-b-stone-300 text-white">
<van-icon name="fire-o" color="#007aff" class="mr-10rpx"/>业务标签
</h2>
<text class="block text-c7" @click="saveCode">{{ editCode ? '' : '' }}</text>
</view>
<view class="flex justify-between border-b-1 border-b-stone-300 items-center px-32rpx py-20rpx">
<h2 class="title-doc-block text-base font-bold! text-c2"><van-icon name="fire-o" color="#007aff" class="mr-10rpx"/>跟进时间轴</h2>
<div v-if="editCode == false">
<view v-for="item in formData.organizeEntities">
<h2 class="title-doc-block mt-32rpx py-24rpx px-32rpx fs-14">{{ item.name }}</h2>
<view v-if="formData.clueLabelName" class="px-32rpx flex flex-wrap">
<view v-for="it in JSON.parse(formData.clueLabelName)" class="block-button mr-16rpx mb-16rpx" :class="{'tag-active': it == 3}">{{ it }}</view>
</view>
</view>
</div>
<div v-else>
<view v-for="item in formData.organizeEntities">
<h2 class="title-doc-block mt-32rpx py-24rpx px-32rpx fs-14">{{ item.name }}</h2>
<view class="px-32rpx flex flex-wrap">
<view v-for="it in item.labelEntityList" class="block-button mr-16rpx mb-16rpx" :class="{'tag-active': compareId(it.id)}" @click="addClueCode(it.id, it.labelName)">{{ it.labelName }}</view>
</view>
</view>
</div>
<view class="flex justify-between border-b-1 border-b-stone-300 items-center px-32rpx py-20rpx bg-teal-500">
<h2 class="title-doc-block text-base font-bold! text-c2 border-b-stone-300 text-white">
<van-icon name="fire-o" color="#007aff" class="mr-10rpx"/>线索阶段
</h2>
</view>
<van-field
v-model="formData.clueStageName"
is-link
readonly
label="阶段"
placeholder="选择线索阶段"
@click="showcluePicker = true"
/>
<div v-if="formData.otherClue">
<view class="flex justify-between border-b-1 border-b-stone-300 items-center px-32rpx py-20rpx bg-teal-500">
<h2 class="title-doc-block text-base font-bold! text-white"><van-icon name="fire-o" color="#007aff" class="mr-10rpx"/>自定义属性</h2>
</view>
<van-cell-group>
<van-cell v-for="(it, key) in JSON.parse(formData.otherClue)" :title="key" :value="it" />
</van-cell-group>
</div>
<van-cell title="备注" :label="formData.remark" />
<!-- <view class="flex items-center v_cell px-32rpx" @click.stop="showcluePicker = true">
<view class="van-cell__title van-field__label"><label>阶段</label></view>
<view class="van-cell__value van-field__value text-left text-c2">{{ formData.clueStageName }}</view>
<i class="van-badge__wrapper van-icon van-icon-arrow van-cell__right-icon"></i>
</view> -->
<!-- <view class="flex justify-between border-b-1 border-b-stone-300 items-center px-32rpx py-20rpx bg-teal-500">
<h2 class="title-doc-block text-base font-bold! text-white"><van-icon name="fire-o" color="#007aff" class="mr-10rpx"/>跟进时间轴</h2>
<text class="block text-c7" @click="addProp = true">添加记录</text>
</view>
<view>
@ -62,9 +180,8 @@ const addRecord = () => {
</view>
</van-step>
</van-steps>
</view>
</view>
<van-popup v-model:show="addProp" :style="{ width: '80%', borderRadius: '7px' }">
</view> -->
<van-popup v-model:show="addProp" :style="{ width: '80%', borderRadius: '7px' }">
<view>
<text class="title-doc-block text-base font-bold! block text-center pt-30rpx mt-15rpx">添加记录</text>
<van-form ref="refForm" @submit="onRecordSubmit">
@ -103,7 +220,16 @@ const addRecord = () => {
</div>
</van-form>
</view>
</van-popup>
</van-popup>
<van-popup v-model:show="showcluePicker" round position="bottom">
<van-picker
:columns="formData.clueStageEntities"
@cancel="showcluePicker = false"
:columns-field-names="customFieldName"
@confirm="onConfirm"
/>
</van-popup>
</view>
</template>
<style scoped lang="scss">
@ -143,6 +269,5 @@ const addRecord = () => {
box-shadow: 0 2px 8px 0 rgba(0,0,0,.1);
margin-top: 15rpx;
color: $uni-text-color-placeholder;
}
</style>

@ -1,19 +1,3 @@
<!--
* @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @Date: 2023-07-28 16:10:52
* @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @LastEditTime: 2023-08-04 16:22:45
* @FilePath: \byhl-zt-app\src\pages\index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<!--
* @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @Date: 2023-07-28 16:10:52
* @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
* @LastEditTime: 2023-08-04 14:04:15
* @FilePath: \byhl-zt-app\src\pages\index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup lang="ts">
import {
ref
@ -21,7 +5,6 @@
import { parseTime } from '@/utils/index'
import { Navigate } from '@/utils/index'
import { Modal } from '@/plugins/Modal'
//
import AssetsList from '@/components/home/AssetsList.vue'
import {
useUserStore
@ -36,19 +19,32 @@
callback: () => behavioraback
}
let searchObj = reactive({
searchValue: '',
nid: '',
searchDate: ''
})
let searchEl: any = ref(null)
let searchEl = ref<InstanceType<typeof AssetsList>>()
let pickerProp = ref<boolean>(false)
let behaviorProp = ref<boolean>(false)
let currentDate = ref<string[]>([])
let behavioractions = ref<behaviora[]>([])
let minDate = ref<Date>(new Date(2022, 0, 1))
let maxDate = ref<Date>(new Date(2026, 8, 1))
let actionShow = ref<boolean>(false)
onMounted(() => {
store.initCount()
})
watch(() => store.readCount, (nval) => {
uni.setTabBarBadge({
index: 0,
text: nval
})
},
{
deep: true
}
)
const searchClick = () => {
searchEl.value.onLoad()
searchEl.value?.initListData('search')
actionShow.value = false
}
const moreExpand = () => {
actionShow.value = true
@ -59,15 +55,16 @@
const initSerach = () => {
searchObj.searchDate = ''
}
const openDetail = (id: string) => {
Navigate.to('/pages/common/assetsDetail/index', { params: { id: id} })
const openDetail = (id: number) => {
Navigate.to('/pages/common/assetsDetail/index', { params: { id } })
}
const confirmClick =() => {
searchObj.searchDate = currentDate?.value[0] + "/" + currentDate?.value[1] + '/' + currentDate.value[2]
const formatDate = (date: any) => `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
const confirmClick =(values: object[]) => {
const [start, end] = values
searchObj.searchDate = `${formatDate(start)}-${formatDate(end)}`
pickerProp.value = false
}
const cancelPicker = () => {
currentDate.value = []
pickerProp.value = false
}
const clickPlay = (call: string) => {
@ -96,8 +93,6 @@
});
}
}
// //
// const active = ref < KnowledgeType > ('like')
</script>
<template>
@ -107,11 +102,11 @@
<view class="text-center text-white font-bold text-base py-15rpx">客户列表</view>
<van-sticky>
<van-search
v-model="searchObj.searchValue"
v-model="searchObj.nid"
show-action
shape="round"
background="#fff"
placeholder="请输入搜索关键词"
placeholder="请输入搜索号码"
>
<template #action>
<view class="flex items-center">
@ -123,7 +118,7 @@
</van-sticky>
</view>
<view class="px-30rpx assets-list">
<AssetsList ref="searchEl" :searchVal="searchObj.searchValue" @clickDetail="openDetail" @toPlay="clickPlay" />
<AssetsList ref="searchEl" :nid="searchObj.nid" :searchDate="searchObj.searchDate" @clickDetail="openDetail" @toPlay="clickPlay" />
</view>
<van-action-sheet v-model:show="actionShow" title="" cancel-text="" :round="false" :closeable="false" close-on-click-action @cancel="onCancel">
<view class="content">
@ -138,18 +133,10 @@
</van-cell>
<view class="flex justify-around my-20rpx">
<van-button type="default" class="w-40vw" @click="initSerach"></van-button>
<van-button type="primary" class="w-40vw">搜索</van-button>
<van-button type="primary" class="w-40vw" @click="searchClick"></van-button>
</view>
</view>
<van-date-picker
v-model="currentDate"
v-show="pickerProp"
title="选择日期"
:min-date="minDate"
:max-date="maxDate"
@confirm="confirmClick"
@cancel="cancelPicker"
/>
<van-calendar v-model:show="pickerProp" type="range" @confirm="confirmClick" @cancel="cancelPicker" />
</view>
</van-action-sheet>
<van-action-sheet
@ -176,8 +163,6 @@
::v-deep .van-search__action:active {
background-color: initial;
}
.home-page {
}
.assets-list {
margin-top: -60rpx;
}

@ -14,7 +14,9 @@
import { LoginResult } from '@/types/user'
import { SliderCaptcha as LoginCaptcha } from '@/components/Captcha/index'
import type { LoginFormInstance } from '@/components/Captcha/types'
import { accountLogin } from '@/api/login'
import { accountLogin, getCodeData } from '@/api/login'
import socket from "@/utils/socketTask";
import { getToken } from "@/utils/Storage"
const store = useUserStore()
//
const enableLoginCaptcha = true
@ -28,14 +30,13 @@
const loginForm = reactive({
username: '',
password: '',
code: '',
code: null,
uuid: '',
mobile: ''
})
const codeUrl = ref('')
const captchaEnabled = ref(false)
//
const pwdLogin = async () => {
// await UserStore.Login(loginForm).catch(err => {
@ -50,12 +51,19 @@
showSuccessToast('登录成功')
Navigate.reLaunch('/pages/index')
}
onMounted(() => {
if (getToken()) {
uni.switchTab({
url: '/pages/index'
});
}
})
//
// const getCode = async () => {
// const res = await LoginService.getCodeImg()
// codeUrl.value = `data:image/gif;base64,${res.data.img}`
// loginForm.uuid = res.data.uuid
// }
const getCode = async () => {
const res = await getCodeData()
codeUrl.value = res.data.img
loginForm.uuid = res.data.uuid
}
//
const handleLogin = async () => {
if (loginForm.mobile === '') {
@ -63,46 +71,51 @@
} else if (loginForm.password === '') {
Modal.msg('请输入您的密码')
} else {
enableLoginCaptcha ? loginCaptchaRef.value?.show() : handleSubmit()
// enableLoginCaptcha ? loginCaptchaRef.value?.show() : handleSubmit()
handleSubmit()
// pwdLogin()
}
}
const handleSubmit = (captchaId? :string) => {
loginLoading.value = true
return new Promise((resolve, reject) => {
doLogin(captchaId).then((res: any) => {
console.log("🚀 ~ file: login.vue:75 ~ doLogin ~ res:", res)
doLogin().then((res: any) => {
isLoginError.value = false
setStore(res)
Navigate.reLaunch('/pages/index')
uni.switchTab({
url: '/pages/index'
});
})
.catch((err: { response: any }) => {
console.log("🚀 ~ file: login.vue:80 ~ returnnewPromise ~ err:", err)
isLoginError.value = true
Modal.msg(((err.response || {}).data || {}).error || '请求出现错误,请稍后再试')
getCode()
Modal.msg(((err.response || {}).data || {}).message || '请求出现错误,请稍后再试')
}).finally(() => {
loginLoading.value = false
})
})
}
const doLogin = (captchaId?: string) => {
const doLogin = () => {
return accountLogin({
grant_type: 'password',
username: loginForm.mobile,
password: passEncrypt(loginForm.password), //
captchaId // id
uuid: loginForm.uuid, // id
code: loginForm.code
})
}
/** 存储登录信息 */
const setStore = (res: LoginResult) => {
const userStore = useUserStore()
// token
userStore.accessToken = res.access_token
// userStore.accessToken = res.access_token
store.setTokens(res.access_token)
//
const info = res.info
const roleCodes = res.attributes?.roleCodes || []
const permissions = res.attributes?.permissions || []
userStore.saveUser({
// socket
socket.initWebSocketTask()
store.saveUser({
...info,
roleCodes,
permissions
@ -118,7 +131,7 @@
}
onLoad(async () => {
// getCode()
getCode()
})
// UserService.getTask('/api/user/list')?.abort()
@ -139,14 +152,16 @@
<input v-model="loginForm.mobile" class="input" type="text" placeholder="请输入账号" maxlength="30">
</view>
<view class="input-item flex align-center">
<view class="iconfont icon-password icon" />
<v
iew class="iconfont icon-password icon" />
<input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20">
</view>
<!-- <view class="input-item flex align-center">
<view class="input-item flex align-center">
<view class="iconfont icon-code icon" />
<input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4">
<image :src="codeUrl" class="login-code-img" @click="getCode" />
</view> -->
</view>
<view class="action-btn">
<van-button :loading="loginLoading" type="primary" class="w80vw mt-6vh!" @click="handleLogin">
登录
@ -166,7 +181,7 @@
</text>
</view>
<!-- 登陆验证码 -->
<login-captcha v-if="enableLoginCaptcha" ref="loginCaptchaRef" @success="handleSubmit" />
<!-- <login-captcha v-if="enableLoginCaptcha" ref="loginCaptchaRef" @success="handleSubmit" /> -->
</view>
</template>

@ -7,30 +7,32 @@
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup lang="ts">
import {Navigate} from "@/utils";
import { useUserStore } from '@/store/user'
import { Modal } from '@/plugins/Modal'
import { imgUrl } from '@/config/env.ts'
const avatar = imgUrl
const { userInfo } = useUserStore()
// import { imgUrl } from '@/config/env.ts'
import store from '@/utils/socketTask'
// const avatar = imgUrl
const { userInfo, setTokens } = useUserStore()
const handleToLogin = async () => {
let isConfirm = await Modal.confirm('确认退出登录吗')
if (isConfirm) {
Navigate.to('/pages/login')
setTokens('')
store.socketTaskClose()
uni.reLaunch({
url: '/pages/login'
});
}
}
onLoad (() => {
// console.log('llllllll=', avatar + userInfo?.avatar)
})
// const handleToInfo = () => {
// Navigate.to('/pages/mine/info/index')
// }
</script>
<template>
<view class="mine-container m-30rpx bg-white">
<view class="pt-150rpx">
<image :src="avatar + '/' + userInfo?.avatar" class="w-160rpx h-160rpx border block m-auto rounded-full" />
<image :src="'http://39.100.77.21:8001/avatar/' + userInfo?.avatar" class="w-160rpx h-160rpx border block m-auto rounded-full" />
</view>
<van-cell-group>
<van-cell title="账号名称" :value="userInfo?.nickname" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -7,9 +7,10 @@
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import type { User } from '@/types/user'
import { setToken } from '@/utils/Storage'
import { setToken, removeToken } from '@/utils/Storage'
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { getReadCount } from "@/api/home";
export interface UserInfo extends User {
roleCodes: string[]
permissions: string[]
@ -20,6 +21,7 @@ export const useUserStore = defineStore(
// 声明变量
const userInfo = ref<User>()
let accessToken = ref()
let readCount = ref()
// 设置用户信息,登录后使用
const saveUser = (u: UserInfo) => {
userInfo.value = u
@ -28,11 +30,22 @@ export const useUserStore = defineStore(
const delUser = () => {
userInfo.value = undefined
}
const setToken = (token: string) => {
const setTokens = (token: string) => {
accessToken.value = token
setToken(token)
if (token) {
setToken(token)
} else {
removeToken()
}
}
return { userInfo, saveUser, delUser, accessToken }
const setReadCount = (num: number) => {
readCount.value = num
}
const initCount = async () => {
const { data } = await getReadCount()
readCount.value = data
}
return { userInfo, readCount, accessToken, saveUser, delUser, setTokens, initCount, setReadCount }
},
{ persist: true }
)

@ -10,18 +10,29 @@
// export type KnowledgeType = 'like' | 'recommend' | 'fatReduction' | 'food'
//通用的泛型工具
export type PageData<T> = {
pageTital: number
total: number
rows: T
records: T
}
// 1、资源对象
export type AssetsObj = {
id: number
name: string
state: number
lastTime: number
call: string
tag: string[]
assignedBy: number
assignedName: string
clueId: number
clueLabel: string
clueLabelList: any[]
clueLabelName: string
clueRecordId: number | null
clueStageEntities: any[]
clueStageId: number | null
clueStageName: string
clueTime: string
createTime: string
nid: string
isNewClue?: string
organizeEntities: any[]
originName: string
otherClue: string
remark: string
}
// 资源列表
@ -34,43 +45,41 @@ export type AssetsPage = PageData<AssetsList>
// total: number // 总条数
// rows: KnowledgeList // 文章列表数组
// }
// 文章列表查询参数
// export type KnowledgeParams = {
// type: KnowledgeType
// current: number
// pageSize: number
// }
//2、关注医生
// 通用的分页查询参数
export type PageParams = {
current: number // 当前页
pageSize: number // 一页多少条
searchVal?: string | number
page: number // 当前页
size: number // 一页多少条
sort?: 'createTime,desc'
}
// 文章列表查询参数
export type KnowledgeParams = PageParams
// 医生卡片
export type Doctor = {
id: string
name: string
avatar: string
hospitalName: string
gradeName: string // 医院等级
depName: string // 科室
positionalTitles: string // 职称
likeFlag: 0 | 1 // 是否关注医生
serviceFee: number // 接诊服务费
consultationNum: number // 接诊人数
score: number // 评分
major: string // 主攻方向
}
// 医生列表
export type DoctorList = Doctor[]
// export type DoctorList = Doctor[]
// export type DoctorPage = PageData<DoctorList>
// 医生分页 api返回数据data类型
export type DoctorPage = PageData<DoctorList>
export type HomeSearch = {
clueLabelName?: string
nid?: string | null
startTime?: string | null
endTime?: string | null
}
export type HomeSearchPage = HomeSearch & PageParams
interface clue {
labelName: string
id: number
}
export interface editClue {
clueId: string
clueLabelList?: clue[]
clueStageName?: string
clueStageId?: number
}

@ -17,4 +17,8 @@ export interface LoginResult {
refresh_token: string
scope: string
token_type: string
}
}
export interface LoginObj {
img: string
uuid: string
}

@ -138,7 +138,7 @@ export enum FormatsEnums {
}
export const parseTime = function (
timestamp: number,
timestamp: number | string,
formats: FormatsEnums = FormatsEnums.YMD
): string {
// formats格式包括

@ -0,0 +1,117 @@
import {
useUserStore
} from '@/store/user'
import {
getToken
} from "@/utils/Storage";
import { showToast } from 'vant';
const store = useUserStore()
let timer = null
// let retimer=null
// const socketUrl = window.location.host
const socketUrl = 'ws://39.100.77.21:8000'
let ws: any = null
const connectSocket = () => {
uni.onSocketOpen(function (res) {
console.log('WebSocket连接已打开');
// heartbeatSocket()
})
uni.onSocketError(function (res) {
uni.showToast({
title: '连接中断,请重新登录',
icon: 'none'
})
console.log('WebSocket连接打开失败请检查');
})
uni.onSocketClose(function (res) {
console.log('WebSocket 已关闭!');
});
uni.onSocketMessage((res) => {
console.log("🚀 ~ file: socketTask.ts:26 ~ uni.onSocketMessage ~ res:", res)
let data = JSON.parse(res.data as string)
if (data.type === 'announcement-push') {
showToast(data.content)
store.setReadCount(store.readCount + 1)
}
})
}
const heartbeatSocket = () => {
timer = setInterval(()=>{
uni.sendSocketMessage({
data:'isConnact',
success:res=>{
if (res.errMsg == 'sendSocketMessage:ok') {
console.log('心跳包')
} else {
heartbeatSocket()
}
}
})
},5000)
}
export default {
// 连接webSocket
initWebSocketTask() {
return new Promise((resolve, reject) => {
if ( ws == null) {
ws = uni.connectSocket({
url: `${socketUrl}/ws?access_token=${getToken()}`,
success(data) {
console.log("websocket连接成功");
resolve(true)
connectSocket()
},
})
}
})
},
connectSocket() {
// this.socketTask=uni.connectSocket({
// url: 'wss://im-api.q3z3.com/ws?Authorization=' + uni.getStorageSync('Authorization'),
// complete: () => {}
// });
// this.socketTask.onOpen(res => {
// console.log('WebSocket连接已打开');
// // 设置心跳
//
// })
// // 监听接收
// this.socketTask.onMessage(res => {
// if(res.data=='ok'){
// return
// }
// var data = JSON.parse(res.data);
// fc.getPush(data);
// console.log('WebSocket接收消息');
// })
// // 监听关闭
// this.socketTask.onClose(res => {
// console.log('WebSocket连接已关闭');
// let token= uni.getStorageSync('Authorization');
// if(this.socketTask&&token){
// this.socketTaskClose()
// retimer=setTimeout(()=>{
// this.connectSocket()
// },5000)
// }
// })
// // 监听异常
// this.socketTask.onError(res => {
// console.log('WebSocket连接打开失败正在尝试重新打开');
// if(this.socketTask){
// this.socketTaskClose()
// retimer=setTimeout(()=>{
// this.connectSocket()
// },5000)
// }
// });
},
// 关闭WebSocket
socketTaskClose() {
uni.closeSocket({})
timer = null
ws = null
console.log('关闭WebSocket');
}
}
export { ws }

@ -23,10 +23,12 @@ export default defineConfig({
server: {
open: true,
cors: true,
host: '0.0.0.0',
proxy: {
"^/api": { //服务器接口路径地址,根据路径设置
target: 'http://172.18.0.225:8000', //你的服务器地址
target: 'http://39.100.77.21:8000', //你的服务器地址
changeOrigin: true, // 允许跨域
ws: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save