first commit

This commit is contained in:
chenzhen
2025-07-22 11:21:01 +08:00
commit 09d0e316e1
164 changed files with 7907 additions and 0 deletions

17
src/App.vue Normal file
View File

@@ -0,0 +1,17 @@
<template>
</template>
<script setup>
import { onLaunch } from '@dcloudio/uni-app';
onLaunch((opt) => {
console.log("onLaunch")
})
</script>
<style>
@import '@/static/common.css';
</style>

37
src/api/auth.js Normal file
View File

@@ -0,0 +1,37 @@
import request from "@/utils/request"
// 获取图片
export function getCaptchaImage() {
return request.get({
url: '/api/captchaImage',
})
}
// 获取验证码
export function getVerifyCode(data) {
return request.post({
url: '/api/sendSms',
data
})
}
// 登录
export function login(data) {
return request.post({
url: '/api/user/login',
data
})
}
// 获取用户信息
export function getUserInfo(data) {
return request.post({
url: '/api/getUserInfo',
data
})
}

57
src/api/business.js Normal file
View File

@@ -0,0 +1,57 @@
import request from "@/utils/request"
// 搜索查询
export function search(data) {
return request.post({
url: '/api/search',
data
})
}
// 企业日常
export function businessDaily(data) {
return request.post({
url: '/api/businessDaily',
data
})
}
// CRM 列表
export function businessCRMList(data) {
return request.post({
url: '/api/businessCRMList',
data
})
}
// CRM 查询走访报告列表
export function visitorReportList(data) {
return request.post({
url: '/api/visitorReportList',
data
})
}
// CRM 客户列表
export function getGuestList(data) {
return request.post({
url: '/api/guestList',
data
})
}
// CRM 查询走访报告详情
export function visitorReportDetail(data) {
return request.post({
url: '/api/visitorReportDetail',
data
})
}
// CRM 查询走访报告列表
export function mattersList(data) {
return request.post({
url: '/api/mattersList',
data
})
}

49
src/api/home.js Normal file
View File

@@ -0,0 +1,49 @@
import request from "@/utils/request"
// 待办数据查询
export function backBlogCount(data) {
return request.post({
url: '/api/backBlogCount',
data
})
}
// 跑马灯列表
export function swiperList(data) {
return request.post({
url: '/api/swiperList',
data
})
}
// 日程提醒
export function stepData(data) {
return request.post({
url: '/api/stepData',
data
})
}
// 销售任务
export function salesTask(data) {
return request.post({
url: '/api/salesTask',
data
})
}
// 常用服务
export function commonServices(data) {
return request.post({
url: '/api/commonServices',
data
})
}
// 新闻公告
export function newsQueryList(data) {
return request.post({
url: '/api/newsQueryList',
data
})
}

9
src/api/notice.js Normal file
View File

@@ -0,0 +1,9 @@
import request from "@/utils/request"
// 查询通知列表
export function noticeList(data) {
return request.post({
url: '/api/noticeList',
data
})
}

View File

@@ -0,0 +1,111 @@
<template>
<view class="custom-navbar">
<view class="navbar-main" :style="{paddingTop:navBarPaddingTop+'px'}">
<!-- 左侧内容 -->
<view class="navbar-left" @click="handleBack" v-if="leftFlag">
<uni-icons type="left" size="24" color="#fff"></uni-icons>
</view>
<!-- 中间标题 -->
<view class="navbar-title">{{ title }}</view>
<!-- 右侧内容 -->
<view class="navbar-right" v-if="rightFlag">
<slot name="right"></slot>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { getNavBarPaddingTop,getNavBarHeight,getStatusBarHeight} from '@/utils/system.js'
const props = defineProps({
title: String,
leftFlag:true,//默认左侧显示 false-不显示
rightFlag:false,//默认右侧不显示
})
const emit = defineEmits(['back'])
let navBarPaddingTop = ref(0)
let statusBarHeight = ref(0)
let navbarHeight = ref(0)
let navHeight = ref(0)
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop();console.log(navBarPaddingTop.value)
statusBarHeight.value = getStatusBarHeight();
navbarHeight.value = getNavBarHeight();
navHeight.value = navbarHeight.value - statusBarHeight.value
})
const handleBack = () => {
emit('back')
uni.navigateBack()
}
</script>
<style scoped>
.custom-navbar {
width: 750rpx;
position: fixed;
top: 0;
left: 50%;
margin-left:-375rpx;
z-index: 999;
/* #ifdef APP-PLUS */
background: url('@/static/images/bg-Blue-header.png') no-repeat;
background-size:750rpx 160rpx;
height:160rpx;
/* #endif */
/* #ifndef APP-PLUS */
background: url('@/static/images/bg-Blue-header2.png') no-repeat;
background-size:750rpx 116rpx;
height:116rpx;
/* #endif */
}
.navbar-content {
width: 100%;
}
.navbar-main {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
box-sizing: border-box;
color:#fff;
/* #ifdef APP-PLUS */
height:160rpx;
/* #endif */
/* #ifndef APP-PLUS */
height:116rpx;
/* #endif */
}
.navbar-left{
width: 70rpx;
text-align: left;
}
.navbar-title {
flex: 1;
font-size: 36rpx;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color:#fff;
margin-top:-3rpx;
}
.navbar-right{
margin-left: auto;
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<view>
<!-- 搜索框 -->
<uni-search-bar v-model="searchText" @confirm="handleSearch" />
<!-- <view v-if="showSuggestions" class="suggestions-list">
<view
v-for="(item, index) in suggestions"
:key="index"
class="suggestion-item"
@click="selectSuggestion(item)"
>
{{ item }}
</view>
</view> -->
<!-- 筛选条件 -->
<view class="filter-container">
<picker mode="selector" :range="categoryOptions" @change="handleCategoryChange">
<view class="filter-item">{{ categoryText }}</view>
</picker>
<picker mode="selector" :range="sortOptions" @change="handleSortChange">
<view class="filter-item">{{ sortText }}</view>
</picker>
</view>
<!-- 搜索结果 -->
<search-results :list="results" />
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
// const suggestions = ref([])
// const showSuggestions = ref(false)
// const handleInput = debounce(async () => {
// if (!searchText.value.trim()) {
// showSuggestions.value = false
// return
// }
// try {
// const res = await getSuggestions(searchText.value)
// suggestions.value = res
// showSuggestions.value = true
// } catch (error) {
// console.error('获取搜索建议失败:', error)
// }
// }, 300)
const searchText = ref('')
const category = ref(0)
const sort = ref(0)
const categoryOptions = ['全部类别', '电子产品', '服装', '食品']
const sortOptions = ['默认排序', '价格从低到高', '价格从高到低', '销量最高']
const categoryText = computed(() => categoryOptions[category.value])
const sortText = computed(() => sortOptions[sort.value])
const handleSearch = async () => {
const params = {
keyword: searchText.value,
category: category.value,
sort: sort.value
}
// 调用搜索API
const res = await searchAPI(params)
results.value = res.data
}
const handleCategoryChange = (e) => {
category.value = e.detail.value
handleSearch()
}
const handleSortChange = (e) => {
sort.value = e.detail.value
handleSearch()
}
</script>

View File

@@ -0,0 +1,137 @@
<template>
<view class="vertical-step-progress">
<view v-for="(step, index) in steps" :key="index" class="step-item">
<view class="step-connector" :style="{ backgroundColor: index < activeStep ? activeColor : inactiveColor2 }"></view>
<view class="step-icon" :style="{ backgroundColor: index < activeStep ? activeColor : inactiveColor }">
<text v-if="index < activeStep"></text>
<!-- <text v-else>{{ index + 1 }}</text> -->
</view>
<view class="step-content">
<text class="step-title">
<text class="font-blue">{{ step.beginTime }}</text> <text class="font-blue">{{ step.endTime }}</text>
</text>
<text class="step-desc">{{ step.desc }}</text>
</view>
<!-- <view class="btn-round-red" @click="handleDelete(index)">
<uni-icons type="trash" size="20" color="#fff"></uni-icons>
</view> -->
</view>
</view>
</template>
<script setup>
// import { ref } from 'vue';
import { showAlert } from '@/utils/message.js';
const props = defineProps({
steps: {
type: Array,
default: () => []
},
activeStep: {
type: Number,
default: -1
},
activeColor: {
type: String,
default: '#4cd964'
},
inactiveColor: {
type: String,
default: '#ffffff'
},
inactiveColor2:{
type: String,
default: '#E6E6E6'
}
})
const emit = defineEmits(['update:modelValue'])
// 删除日程提醒
const handleDelete = (index) => {
showAlert("是否确认删除?","提示",true,()=>{
props.steps.splice(index,1);
emit('update:modelValue', props.steps)
});
}
</script>
<style scoped>
.vertical-step-progress {
margin: 30rpx 0rpx 0;
}
.step-item {
display: flex;
align-items: flex-start;
position: relative;
}
.step-connector {
position: absolute;
left: 4px;
top: 0px;
width: 2px;
height: 160rpx;
transform:translate(0px, -8px) !important;
}
.step-item:last-child .step-connector {
height: 100rpx;
}
.step-icon {
width:1px;
height: 1px;
padding:3px;
border:2px solid #E6E6E6;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
margin-right: 15px;
color: white;
font-size: 14px;
z-index: 1;
}
.step-content {
flex: 1;
display: flex;
flex-direction: column;
}
.step-title {
font-size: 28rpx;
color: #BFBFBF;
margin-bottom: 4px;
margin-top:-5px;
}
.step-title .font-blue{
color:#05A3F4;
font-weight: bold;
font-size:28rpx;
}
.step-desc{
font-size: 32rpx;
color: #333;
height:120rpx;
width: 500rpx;
}
.step-item:last-child .step-desc{
height:70rpx;
}
.btn-round-red{
background-color: #FF687A;
color:#fff;
border-radius: 50%;
width:56rpx;
height: 56rpx;
line-height: 56rpx;
text-align: center;
margin-top:30rpx;
}
</style>

View File

@@ -0,0 +1,67 @@
<!-- components/customTabs.vue -->
<template>
<view class="tabs-container">
<view class="tabs-scorll">
<view class="tabs-header">
<view v-for="(tab, index) in tabs" :key="index"
:class="['tab-item', { 'active': modelValue === index }]"
@click="switchTab(index)"
>
{{ tab }}
</view>
</view>
</view>
<slot></slot>
</view>
</template>
<script setup>
defineProps({
tabs: {
type: Array,
default: () => []
},
modelValue: {
type: Number,
default: 0
}
})
const emit = defineEmits(['update:modelValue'])
const switchTab = (index) => {
emit('update:modelValue', index)
}
</script>
<style scoped>
.tabs-header {
display: flex;
height: 80rpx;
background: #fff;
border-bottom: 1rpx solid #eee;
}
.tab-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #666;
}
.tab-item.active {
color: #007AFF;
position: relative;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background-color: #007AFF;
}
</style>

View File

@@ -0,0 +1,461 @@
<template>
<!-- <view class="uni-select-dc" :style="{ 'z-index': zindex }"> :style="{ 'z-index': zindex }"-->
<view class="uni-select-dc">
<view class="uni-select-dc-select" :class="{ active: active }" @click.stop="handleSelect">
<!-- 禁用mask -->
<view class="uni-disabled" v-if="disabled"></view>
<!-- 清空 -->
<view class="close-icon close-postion" v-if="realValue.length && !active && !disabled && showClearIcon">
<text @click.stop="handleRemove(null)"></text>
</view>
<!-- 显示框 -->
<view class="uni-select-multiple" v-show="realValue.length">
<block v-if="multiple" >
<view class="uni-select-multiple-item" v-for="(item, index) in changevalue" :key="index">
{{ item.text }}
<view class="close-icon" v-if="showValueClear">
<text @click.stop="handleRemove(index)"></text>
</view>
</view>
</block>
<!-- 单选时展示内容 -->
<view v-else class="single-text">
{{ changevalue.length ? changevalue[0].text : "" }}
</view>
</view>
<!-- 为空时的显示文案 -->
<view v-if="realValue.length == 0 && showplaceholder">{{ placeholder }}</view>
<!-- 右边的下拉箭头 -->
<view :class="{ disabled: disabled, 'uni-select-dc-icon': !downInner, 'uni-select-dc-inner': downInner }">
<text></text>
</view>
</view>
<!-- 下拉选项 -->
<scroll-view class="uni-select-dc-options" :scroll-y="true" v-show="active">
<view class="uni-select-dc-item" :class="{ active: realValue.includes(item[svalue]) }"
v-for="(item, index) in options" :key="index"
@click.stop="handleChange(index, item)"
>
{{ item[slabel] }}
</view>
</scroll-view>
</view>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
const props = defineProps({
// 是否显示全部清空按钮
showClearIcon: {
type: Boolean,
default: false,
},
// 是否多选
multiple: {
type: Boolean,
default: false,
},
// 下拉箭头是否在框内
downInner: {
type: Boolean,
default: true,
},
// 是否显示单个删除
showValueClear: {
type: Boolean,
default: true,
},
zindex: {
type: Number,
default: 999,
},
// 禁用选择
disabled: {
type: Boolean,
default: false,
},
options: {
type: Array,
default() {
return [];
},
},
value: {
type: Array,
default() {
return [];
},
},
placeholder: {
type: String,
default: "请选择",
},
showplaceholder: {
type: Boolean,
default: true,
},
// 默认取text
slabel: {
type: String,
default: "text",
},
// 默认取value
svalue: {
type: String,
default: "value",
},
});
const emit = defineEmits(["change"]);
const active = ref(false); // 组件是否激活,
let changevalue = reactive([]);
let realValue = reactive([]);
onMounted(() => {
init();
});
// 初始化函数
const init = () => {
if (props.value.length > 0) {
props.options.forEach((item) => {
props.value.forEach((i) => {
if (item[props.svalue] === i) {
changevalue.push(item);
}
})
})
realValue = props.value;
console.log("props---", changevalue);
} else {
changevalue = [];
realValue = [];
}
};
// 点击展示选项
const handleSelect = () => {
if (props.disabled) return;
active.value = !active.value;
};
// 移除数据
const handleRemove = (index) => {
if (index === null) {
realValue = [];
changevalue = [];
} else {
realValue.splice(index, 1);
changevalue.splice(index, 1);
}
emit("change", changevalue, realValue);
};
// 点击组件某一项
const handleChange = (index, item) => {
console.log("选中了某一项", index, item);
// 如果是单选框,选中一项后直接关闭
if (!props.multiple) {
console.log("关闭下拉框");
changevalue.length = 0
realValue.length = 0
changevalue.push(item);
realValue.push(item[props.svalue])
active.value = !active.value;
} else {
// 多选操作
const arrIndex = realValue.indexOf(item[props.svalue]);
if (arrIndex > -1) {
// 如果该选项已经选中,当点击后就不选中
changevalue.splice(arrIndex, 1);
realValue.splice(arrIndex, 1);
} else {
// 否则选中该选项
changevalue.push(item);
realValue.push(item[props.svalue]);
}
}
// 触发回调函数
emit("change", changevalue, realValue);
};
</script>
<style lang="scss" scoped>
.uni-select-dc {
position: relative;
// z-index: 999;
.uni-select-mask {
width: 100%;
height: 100%;
}
/* 删除按钮样式*/
.close-icon {
height: 100%;
width: 20px;
display: flex;
align-items: center;
justify-content: center;
// z-index: 3;
cursor: pointer;
text {
position: relative;
background: #c0c4cc;
width: 13px;
height: 13px;
border-radius: 50%;
border: 1px solid #bbb;
&::before,
&::after {
content: "";
position: absolute;
left: 20%;
top: 48%;
height: 1px;
width: 60%;
transform: rotate(45deg);
background-color: #909399;
}
&::after {
transform: rotate(-45deg);
}
}
}
//所有情空的定位
.close-postion {
position: absolute;
right: 35px;
top: 0;
height: 100%;
width: 15px;
}
/* 多选盒子 */
.uni-select-multiple {
display: flex;
flex-wrap: nowrap;
overflow: scroll;
.single-text {
color: #333;
}
.uni-select-multiple-item {
background: #f4f4f5;
margin-right: 5px;
padding: 2px 4px;
border-radius: 4px;
color: #909399;
display: flex;
flex-shrink: 0;
}
}
// select部分
.uni-select-dc-select {
user-select: none;
position: relative;
z-index: 3;
height: 38px;
padding: 0 30px 0 10px;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgb(229, 229, 229);
display: flex;
align-items: center;
font-size: 12px;
color: #999;
min-width: 10px;
.uni-disabled {
position: absolute;
left: 0;
width: 100%;
height: 100%;
z-index: 19;
cursor: no-drop;
background: rgba(255, 255, 255, 0.5);
}
.uni-select-dc-input {
font-size: 14px;
color: #999;
display: block;
width: 96%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 30px;
box-sizing: border-box;
&.active {
color: #333;
}
}
.uni-select-dc-icon {
cursor: pointer;
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 20px;
display: flex;
align-items: center;
justify-content: center;
border-left: 1px solid rgb(229, 229, 229);
text {
display: block;
width: 0;
height: 0;
border-width: 12rpx 12rpx 0;
border-style: solid;
border-color: #bbb transparent transparent;
transition: 0.3s;
}
&.disabled {
cursor: no-drop;
text {
width: 20rpx;
height: 20rpx;
border: 2px solid #ff0000;
border-radius: 50%;
transition: 0.3s;
position: relative;
z-index: 999;
&::after {
content: "";
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 2px;
margin-top: -1px;
background-color: #ff0000;
transform: rotate(45deg);
}
}
}
}
.uni-select-dc-inner {
cursor: pointer;
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 20px;
display: flex;
align-items: center;
justify-content: center;
text {
display: block;
width: 9px;
height: 9px;
position: absolute;
right: 15px;
top: 8px;
border: 1px solid #bbb;
transform: rotate(-45deg);
border-color: transparent transparent#bbb #bbb;
transition: 0.3s;
}
&.disabled {
cursor: no-drop;
text {
width: 20rpx;
height: 20rpx;
border: 2px solid #ff0000;
border-radius: 50%;
transition: 0.3s;
position: relative;
z-index: 999;
&::after {
content: "";
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 2px;
margin-top: -1px;
background-color: #ff0000;
transform: rotate(45deg);
}
}
}
}
// 激活之后图标旋转180度
&.active .uni-select-dc-icon {
text {
transform: rotate(180deg);
}
}
&.active .uni-select-dc-inner {
text {
position: absolute;
right: 10px;
top: 12px;
transform: rotate(-225deg);
}
}
}
// options部分
.uni-select-dc-options {
user-select: none;
position: absolute;
top: calc(100% + 5px);
left: 0;
width: 100%;
// height: 400rpx;
max-height: 400rpx;
border-radius: 4px;
border: 1px solid rgb(229, 229, 229);
background: #fff;
padding: 5px 0;
box-sizing: border-box;
z-index: 9999;
.uni-select-dc-item {
text-align: left;
padding: 0 10px;
box-sizing: border-box;
cursor: pointer;
line-height: 2.5;
transition: 0.3s;
font-size: 14px;
// 取消长按的背景色
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
-webkit-user-select: none;
-moz-user-focus: none;
-moz-user-select: none;
&.active {
color: #409eff;
background-color: #f5f7fa;
&:hover {
color: #409eff;
background-color: #f5f7fa;
}
}
&:hover {
background-color: #f5f5f5;
}
}
}
}
</style>

View File

@@ -0,0 +1,182 @@
<template>
<view class="container">
<!-- 验证区域 -->
<view class="captcha-area">
<image :src="backgroundImg" class="bg-img" mode="widthFix"></image>
<view class="puzzle-hole" :style="{
left: `${holePosition.x}px`,
top: `${holePosition.y}px`,
width: `${puzzleSize.width}px`,
height: `${puzzleSize.height}px`
}"></view>
</view>
<!-- 滑块区域 -->
<view class="slider-area">
<movable-area class="movable-area">
<movable-view class="movable-view" direction="horizontal" :x="sliderX" @change="onDrag"
@touchend="onDragEnd" :damping="40" :friction="10">
<image :src="puzzleImg" class="puzzle-img"></image>
</movable-view>
</movable-area>
<text class="slider-text">{{ sliderText }}</text>
</view>
<!-- 操作按钮 -->
<button class="refresh-btn" @click="initCaptcha">刷新验证码</button>
</view>
</template>
<script>
export default {
data() {
return {
backgroundImg: './static/images/captcha/1.jpg?v=1', // 替换为你的背景图
puzzleImg: '', // 滑块小图
holePosition: { x: 0, y: 0 }, // 缺口位置
puzzleSize: { width: 50, height: 50 }, // 滑块尺寸
sliderX: 0, // 滑块位置
targetX: 0, // 正确目标位置
isVerified: false,
sliderText: '拖动滑块完成拼图'
};
},
mounted() {
this.initCaptcha();
},
methods: {
// 初始化验证码
initCaptcha() {
this.isVerified = false;
this.sliderX = 0;
this.sliderText = '拖动滑块完成拼图';
// 随机生成缺口位置 (示例)
const maxX = 300 - this.puzzleSize.width;
const maxY = 200 - this.puzzleSize.height;
this.holePosition = {
x: Math.floor(Math.random() * maxX),
y: Math.floor(Math.random() * maxY)
};
// 这里应该从服务器获取裁剪后的小图
// 实际项目中需要通过API获取
this.puzzleImg = this.generatePuzzleImage();
// 设置目标位置 (映射到滑块轨道)
this.targetX = this.mapToSliderPosition(this.holePosition.x);
},
// 模拟生成滑块图片 (实际应从服务端获取)
generatePuzzleImage() {
// 这里应该是从背景图裁剪的图片
return './static/images/captcha/1-1.jpg?v=1';
},
// 映射背景位置到滑块位置
mapToSliderPosition(bgX) {
const bgWidth = 300; // 背景图显示宽度
const sliderWidth = 280; // 滑块轨道宽度
return (bgX / bgWidth) * sliderWidth;
},
// 拖动中
onDrag(e) {
if (this.isVerified) return;
this.sliderX = e.detail.x;
},
// 拖动结束
onDragEnd() {
if (this.isVerified) return;
// 允许的误差范围
const tolerance = 5;
if (Math.abs(this.sliderX - this.targetX) < tolerance) {
this.isVerified = true;
this.sliderText = '验证成功 ✓';
uni.showToast({ title: '验证成功', icon: 'success' });
// 这里可以触发验证通过后的操作
} else {
this.sliderText = '验证失败,请重试';
this.sliderX = 0;
setTimeout(() => {
if (!this.isVerified) this.sliderText = '拖动滑块完成拼图';
}, 1000);
}
}
}
};
</script>
<style>
.container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.captcha-area {
position: relative;
width: 300px;
height: 200px;
border: 1px solid #eee;
border-radius: 8px;
overflow: hidden;
margin-bottom: 30px;
}
.bg-img {
width: 100%;
height: 100%;
}
.puzzle-hole {
position: absolute;
border: 2px dashed #fff;
box-shadow: 0 0 0 2000px rgba(0, 0, 0, 0.5);
pointer-events: none;
}
.slider-area {
width: 300px;
position: relative;
}
.movable-area {
width: 100%;
height: 50px;
background-color: #f5f5f5;
border-radius: 25px;
}
.movable-view {
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
.puzzle-img {
width: 50px;
height: 50px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.slider-text {
display: block;
text-align: center;
margin-top: 10px;
color: #666;
}
.refresh-btn {
margin-top: 20px;
width: 200px;
}
</style>

2
src/enums/cacheEnums.js Normal file
View File

@@ -0,0 +1,2 @@
export const TOKEN_KEY = 'token';
export const AGREEWELCOME_KEY="agreewelcome"

21
src/enums/requestEnums.js Normal file
View File

@@ -0,0 +1,21 @@
export const ContentTypeEnum = {
JSON: 'application/json;charset=UTF-8'
};
export const RequestMethodsEnum = {
GET: 'GET',
POST: 'POST',
DELETE: 'DELETE',
PUT: 'PUT'
};
export const RequestCodeEnum = {
SUCCESS: 0, //成功
FAILED: -1, // 失败
TOKEN_INVALID: 10003 // TOKEN失效未登录
};
export const RequestErrMsgEnum = {
ABORT: 'request:fail abort',
TIMEOUT: 'request:fail timeout'
};

19
src/main.js Normal file
View File

@@ -0,0 +1,19 @@
import { createSSRApp } from "vue";
import App from "./App.vue";
import '@/static/font/iconfont.css'
// pinia
import { createPinia } from 'pinia'
const pinia = createPinia()
export function createApp() {
const app = createSSRApp(App);
app.use(pinia)
return {
app,
};
}

91
src/manifest.json Normal file
View File

@@ -0,0 +1,91 @@
{
"id" : "H5E705637",
"name" : "app",
// "appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"compatible" : {
"ignoreVersion" : true //true表示忽略版本检查 提示框HBuilderx1.9.0及
},
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"icons" : {
"android" : {
"hdpi" : "static/icons/72x72.png",
"xhdpi" : "static/icons/96x96.png",
"xxhdpi" : "static/icons/144x144.png",
"xxxhdpi" : "static/icons/192x192.png"
}
}
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3",
"h5" : {
"router" : {
"base" : ""
/*,"mode": "history" H5 */
},
"template" : "index.html"
}
}

146
src/pages.json Normal file
View File

@@ -0,0 +1,146 @@
{
"easycom": {
"autoscan": true,
"custom": {
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"pages": [
{
"path": "pages/loading/loading",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/home/home",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/business",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/visitorReport",//市场信息管理
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/visitorReportAdd",//走访报告添加
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/visitorReportDetail",//走访报告详情
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/visitorReportEnter",//走访报告内容录入
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/marketInformation",//市场信息管理
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/weekPlanUpdate",//修改周计划
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/vistorCheckin",//签到打卡
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/checkinStatistics",//打卡统计
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/business/CRM/paymentCollection",//回款查看
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/notice/notice",
"style": {
"navigationBarTitleText": "",
"app-plus" : {
"bounce" : "none" // 取消APP端iOS回弹,避免与下拉刷新冲突 (可统一配在 'globalStyle')
},
"mp-alipay":{"allowsBounceVertical":"NO"} // 取消支付宝和钉钉小程序的iOS回弹,避免与下拉刷新冲突 (可统一配在 'globalStyle')
}
},
{
"path": "pages/userinfo/userinfo",
"style": {
"navigationBarTitleText": ""
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "718友晟",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8",
"navigationStyle": "custom"
},
"tabBar": {
"color": "#919191",
"selectedColor": "#ffffff",
"borderStyle": "#ffffff",
"backgroundColor": "#000000",
"fontSize": "12px",
"iconWidth": "24px",
"list": [
{
"pagePath": "pages/home/home",
"iconPath": "static/images/tabs/menu-home.png",
"selectedIconPath": "static/images/tabs/menu-home-on.png",
"text": "首页"
},
{
"pagePath": "pages/business/business",
"iconPath": "static/images/tabs/menu-business.png",
"selectedIconPath": "static/images/tabs/menu-business-on.png",
"text": "业务中心"
},
{
"pagePath": "pages/notice/notice",
"iconPath": "static/images/tabs/menu-info.png",
"selectedIconPath": "static/images/tabs/menu-info-on.png",
"text": "消息"
},
{
"pagePath": "pages/userinfo/userinfo",
"iconPath": "static/images/tabs/menu-me.png",
"selectedIconPath": "static/images/tabs/menu-me-on.png",
"text": "我的"
}
]
}
}

View File

@@ -0,0 +1,234 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'打卡统计'" :leftFlag="true" :rightFlag="false"></customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 正文内容 -->
<view>
<!-- 搜索 -->
<view class="search">
<picker @change="bindPickerChange" :value="cityIndex" :range="cityArr" class="picker-bg">
<view class="picker">
<uni-icons type="location" size="18"></uni-icons>
<view>{{cityArr[cityIndex]}}</view>
<uni-icons type="down" size="18"></uni-icons>
</view>
</picker>
<picker mode="date" :value="defaultDate" :start="startDate" :end="endDate"
@change="bindDateChange" class="picker-bg">
<view class="picker">
<uni-icons custom-prefix="iconfont" color="#ffffff" type="icon-phoneshizhong" size="18"></uni-icons>
<view>{{defaultDate}}</view>
<uni-icons type="down" size="18"></uni-icons>
</view>
</picker>
<button type="default" @click="handleSearch" size="mini" class="btn-search">查询</button>
</view>
<!-- 签到打卡 -->
<view class="checkin-tab">
<view class="checkin-tab-item" :class="{active:tabType==0}" @click="handleTab(0)">
<view class="tab-item-title">7</view>
<view class="tab-item-name">最新签到打卡</view>
</view>
<view class="checkin-tab-item" :class="{active:tabType==1}" @click="handleTab(1)">
<view class="tab-item-title">4</view>
<view class="tab-item-name">未签到打卡</view>
</view>
</view>
<!-- tab切换显示 -->
<view class="white-bg">
<!-- 最新签到列表 -->
<view class="tab-con" v-if="tabType==0">
<view class="tab-title" v-for="(item,index) in list1" :key="index">
{{item}}
<uni-icons type="checkbox-filled" size="26" color="#02C74C"></uni-icons>
</view>
</view>
<!-- 未签到列表 -->
<view class="tab-con" v-if="tabType==1">
<view class="tab-title" v-for="(item,index) in list2" :key="index">
{{item}}
<uni-icons type="info-filled" size="26" color="#FF8059"></uni-icons>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import { getDate} from '@/utils/datetime.js'
let cityIndex = ref(0);
let cityArr = ["北京大区","天津大区"];
// 选择大区列表
let bindPickerChange = (e)=>{
console.log('picker发送选择改变携带值为', e.detail.value)
cityIndex.value = e.detail.value
}
// 开始时间
let startDate = getDate('start');
// 结束时间间隔10年
let endDate = getDate('end');
let defaultDate = getDate({ format: true })
let bindDateChange = (e) =>{
defaultDate = e.detail.value
}
let searchValue = ref(null)
// 查询搜索跳转
let handleSearch = () => {
console.log(searchValue.value)
}
// tab 切换
let tabType = ref(0)
let handleTab = (type)=>{
tabType.value = type;
}
// 列表
let list1 = ref([]);
list1.value = ["王振","胡本华","高勐","崔礼涛","汪津春","王永健","张浩"];
let list2 = ref([]);
list2.value = ["付翰","张旭光","赵欣鸣","张王小北"];
// 确定
let handleSubmit = () => {
}
</script>
<style scoped>
.search {
display: flex;
padding:0 30rpx;
}
.search .btn-search {
border: none;
background: none;
line-height: normal;
color: #fff;
line-height: 56rpx !important;
padding: 10rpx 0 0;
text-align: left;
cursor: pointer;
}
.search .btn-search::after {
display: none;
}
.search .picker-bg{
display: flex;
background-color: #6FA2F8;
border-radius: 25px;
color:#fff;
font-size:28rpx;
padding:0rpx 20rpx;
/* #ifndef APP-PLUS */
padding:10rpx 20rpx 0 20rpx;
/* #endif */
margin-right:20rpx;
}
.search .picker-bg .picker {
display: flex;
align-items: center;
/* #ifndef APP-PLUS */
padding-top:2rpx;
/* #endif */
}
.search .picker-bg .picker .uni-icons{
color:#fff !important;
}
.search .picker-bg .picker .uni-icons:first-child{
margin-right: 10rpx;
}
.search .picker-bg .picker .uniui-down{
margin-left: 20rpx;
}
.checkin-tab{
margin-top:35rpx;
display: flex;
padding:0 30rpx;
flex: 1;
}
.checkin-tab-item{
align-items: center;
justify-content: center;
color:rgba(255, 255, 255, 0.5);
text-align: center;
margin-right:30rpx;
font-weight: bold;
padding-bottom:20rpx;
}
.checkin-tab-item.active{
position: relative;
color:#fff;
}
.checkin-tab-item.active::after{
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
background-color: #fff;
width: 100rpx;
height: 8rpx;
}
.checkin-tab-item .tab-item-title{
font-size:60rpx;
}
.white-bg {
width: 670rpx;
margin: 0;
border-radius: 8px 8px 0 0;
padding:50rpx 40rpx;
margin-top:20rpx;
/* #ifdef APP-PLUS */
height:calc(100vh - 260px);
/* #endif */
/* #ifndef APP-PLUS */
height:calc(100vh - 230px);
/* #endif */
}
.tab-con{
display: flex;
flex-flow: row wrap
}
.tab-con .tab-title{
border:1px solid #E8E8E8;
text-align: center;
border-radius: 5px;
position: relative;
/* padding:10rpx 20rpx; */
margin-right:20rpx;
margin-bottom:30rpx;
width:148rpx;
height: 60rpx;
line-height: 60rpx;
font-size: 28rpx;
}
.tab-con .tab-title:nth-child(4n){
margin-right: 0;
}
.tab-con .uni-icons{
position: absolute;
right:-10px;
top:-10px;
}
</style>

View File

@@ -0,0 +1,224 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'市场信息管理'" :leftFlag="true" :rightFlag="true">
<template #right>
<view class="head-right" @click="handleAdd">
<uni-icons type="plus" size="24" color="#B7D2FF"></uni-icons>新增
</view>
</template>
</customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height" :style="{ paddingTop: navBarPaddingTop + 'px' }"></view>
<!-- 正文内容 -->
<view class="all-body market-con">
<view class="nav-list">
<view class="nav-item" :class="{active:index==activeTab}"
v-for="(item,index) in tabList" :key="index"
@click="handleNav(index)"
>{{ item }}</view>
</view>
<!-- 分页部分 -->
<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"
:up="upOption" :down="downOption" :fixed="false" textColor="#ffffff" bgColor="#ffffff"
class="scroll-h" :class="{'loading-scroll':cssFlag}">
<!-- 市场机会信息 -->
<block v-if="activeTab === 0">
市场机会信息内容
</block>
<!-- 重大事项信息 -->
<block v-if="activeTab === 1">
<view class="white-bg margin-bottom20" v-for="(item, index) in list" :key="index">
<view class="report-list">
<view class="title r-list">
<view class="r-left" style="font-size:38rpx;">{{ item.title }}</view>
<view class="r-right btn-gray flex-auto" :class="{'btn-blue':item.status==2}" size="mini">{{ item.statusName }}</view>
</view>
<view style="padding:0rpx 0 10rpx">
<view class="font-bold" style="padding-bottom:10rpx">产生影响</view>
<view class="font-gray">{{ item.desc }}</view>
</view>
</view>
</view>
</block>
</mescroll-uni>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
import { getNavBarPaddingTop } from '@/utils/system.js'
import { mattersList } from '@/api/business.js'
// 获取导航栏高度用于内容区域padding
const navBarPaddingTop = ref(0);
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop() * 2;
})
const activeTab = ref(1);// 默认重大事项信息
const tabList = ['市场机会信息', '重大事项信息', '竞争对手信息', '人员变动信息','多标签效果','多标签效果'];
let handleNav = (index)=>{
console.log(index)
activeTab.value = index;
}
// 新增
let handleAdd = () => {
// uni.navigateTo({ url: '/pages/business/CRM/visitorReportAdd' })
}
// 查询列表
let list = ref([]);
const mescrollRef = ref(null);
const upOption = ref({
page: { num: 0, size: 10 },
noMoreSize: 5,
empty: { tip: '~ 空空如也 ~' },
textLoading: '加载中...',
textNoMore: '已经到底了'
});
const downOption = ref({
auto: true,
textInOffset: '下拉刷新',
textOutOffset: '释放更新',
textLoading: '刷新中...'
});
let cssFlag=ref(false);//控制样式
const mescrollInit = (mescroll) => {
cssFlag.value = true;
mescrollRef.value = mescroll;
};
// 下拉刷新
const downCallback = async (mescroll) => {
try {
setTimeout(async () => {
const res = await getMattersList(1, upOption.value.page.size);
cssFlag.value = false;
list.value = res.list;
mescroll.resetUpScroll();
}, 500);
} catch (error) {
mescroll.endErr();
} finally {
setTimeout(async () => {
mescroll.endSuccess();
}, 500);
}
}
// 上拉加载更多
const upCallback = async (mescroll) => {
try {
setTimeout(async () => {
const res = await getMattersList(mescroll.num, mescroll.size);
if (mescroll.num === 1) {
list.value = res.list;
} else {
list.value.push(...res.list);
}
mescroll.endBySize(res.list.length, res.total);
}, 500);
} catch (error) {
mescroll.endErr();
}
}
// 获取数据列表
const getMattersList = (pageIndex, pageSize) => {
return new Promise(async (resolve) => {
let param = {
pageIndex,
pageSize
}
let res = await mattersList(param);
resolve({
list: res.list,
total: res.totalCount
});
});
}
</script>
<style scoped>
.all-body{
/* #ifdef APP-PLUS */
top:88rpx;
height: calc(100vh - 44px);
/* #endif */
/* #ifndef APP-PLUS */
top:100rpx;
height: calc(100vh - 48px);
/* #endif */
}
/*.market-con{
} */
.nav-list{
display: flex;
flex-wrap: wrap;
/* #ifdef APP-PLUS */
padding:80rpx 30rpx 0;
/* #endif */
/* #ifndef APP-PLUS */
padding:20rpx 30rpx 0;
/* #endif */
}
.nav-list .nav-item{
background-color:#05A3F4;
border-radius: 10rpx;
color:#FFFFFF;
font-size:28rpx;
text-align: center;
padding:10rpx 25rpx;
margin-right:15rpx;
margin-bottom:20rpx;
}
.nav-list .nav-item:nth-child(3n){
margin-right:0;
}
.nav-list .nav-item.active{
background-color: #fff;
color:#3384DF;
font-weight: bold;
}
.report-list .r-list{
align-items:flex-start;
}
.scroll-h {
/* #ifdef APP-PLUS */
height: calc(100vh - 155px);
/* #endif */
/* #ifndef APP-PLUS */
height: calc(100vh - 135px);
/* #endif */
}
.white-bg {
padding-top:10rpx;
padding-bottom: 10rpx;
}
.report-list .title{
display: flex;
}
</style>

View File

@@ -0,0 +1,169 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'个人回款信息统计表'" :leftFlag="true" :rightFlag="false"></customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 正文内容 -->
<view>
<!-- 搜索 -->
<view class="search">
<picker mode="date" :value="defaultDate" :start="startDate" :end="endDate"
@change="bindDateChange" class="picker-bg">
<view class="picker">
<uni-icons custom-prefix="iconfont" color="#ffffff" type="icon-phoneshizhong" size="18"></uni-icons>
<view>{{defaultDate}}</view>
<uni-icons type="down" size="18"></uni-icons>
</view>
</picker>
<picker @change="bindPickerChange" :value="peopleIndex" :range="peopleArr" class="picker-bg">
<view class="picker">
<uni-icons type="person" size="18"></uni-icons>
<view>{{peopleArr[peopleIndex]}}</view>
<uni-icons type="down" size="18"></uni-icons>
</view>
</picker>
<button type="default" @click="handleSearch" size="mini" class="btn-search">查询</button>
</view>
<!-- 回款信息查询 -->
<view class="white-bg">
<view class="table-title">回款信息查询</view>
<table class="my-table">
<tr>
<th class="tab-width100">姓名</th>
<th class="tab-width160">客户名称</th>
<th>目标回款</th>
<th>实际回款</th>
<th>完成度</th>
</tr>
<tr v-for="(item, index) in tableData" :key="index">
<td>{{ item.name }}</td>
<td class="txtLeft">{{ item.guestName }}</td>
<td>{{ item.tragetReturnMoney }}</td>
<td>{{ item.returnMoney }}</td>
<td>{{ item.completRate }}</td>
</tr>
</table>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import { getDate} from '@/utils/datetime.js'
// 开始时间
let startDate = getDate('start');
// 结束时间间隔10年
let endDate = getDate('end');
let defaultDate = getDate({ format: true })
let bindDateChange = (e) =>{
defaultDate = e.detail.value
}
let peopleIndex = ref(0);
let peopleArr = ["张志刚1","张志刚2"];
// 选择列表
let bindPickerChange = (e)=>{
console.log('picker发送选择改变携带值为', e.detail.value)
peopleIndex.value = e.detail.value
}
let searchValue = ref(null)
// 查询搜索跳转
let handleSearch = () => {
console.log(searchValue.value)
}
const tableData = [
{ name: '张志钢',guestName:'航宇救生装备有限公司',tragetReturnMoney:677153.25,returnMoney:677153.25,completRate:'100%'},
{ name: '张志钢',guestName:'中国船舶集团有限公司第七一七研究所',tragetReturnMoney:'700000.00',returnMoney:'53890.00',completRate:'7.7%'},
{ name: '张志钢',guestName:'北京国科舰航传感技术有限公司',tragetReturnMoney:'290877.00',returnMoney:'290877.00',completRate:'100%'},
{ name: '张志钢',guestName:'武汉杭久电气有限公司',tragetReturnMoney:869706.80,returnMoney:537809.75,completRate:'61.8%'},
{ name: '张志钢',guestName:'深圳市欧灿科技有限公司',tragetReturnMoney:328097.10,returnMoney:297905.37,completRate:'90.8%'},
{ name: '张志钢',guestName:'其他',tragetReturnMoney:558703.02,returnMoney:558703.02,completRate:'100%'},
{ name: '张志钢',guestName:'武汉航空仪表有限责任公司',tragetReturnMoney:'9807328.00',returnMoney:8692300.93,completRate:'88.6%'},
{ name: '张志钢',guestName:'武汉永力科技股份有限公司',tragetReturnMoney:539087.21,returnMoney:539087.21,completRate:'100%'},
]
</script>
<style scoped>
.search {
display: flex;
padding:0 30rpx;
}
.search .btn-search {
border: none;
background: none;
line-height: normal;
color: #fff;
line-height: 56rpx !important;
padding: 10rpx 0 0;
text-align: left;
cursor: pointer;
}
.search .btn-search::after {
display: none;
}
.search .picker-bg{
display: flex;
background-color: #6FA2F8;
border-radius: 25px;
color:#fff;
font-size:28rpx;
padding:0rpx 20rpx;
/* #ifndef APP-PLUS */
padding:10rpx 20rpx 0 20rpx;
/* #endif */
margin-right:20rpx;
}
.search .picker-bg .picker {
display: flex;
align-items: center;
/* #ifndef APP-PLUS */
padding-top:2rpx;
/* #endif */
}
.search .picker-bg .picker .uni-icons{
color:#fff !important;
}
.search .picker-bg .picker .uni-icons:first-child{
margin-right: 10rpx;
}
.search .picker-bg .picker .uniui-down{
margin-left: 20rpx;
}
.white-bg {
width: 750rpx;
margin: 0;
border-radius: 8px 8px 0 0;
padding:20rpx 0rpx;
margin-top:20rpx;
/* #ifdef APP-PLUS */
height:calc(100vh - 135px);
/* #endif */
/* #ifndef APP-PLUS */
height:calc(100vh - 112px);
/* #endif */
}
.table-title{
text-align: center;
font-weight: bold;
border-bottom:2px solid #E7E7E7;
padding-bottom:20rpx;
}
</style>

View File

@@ -0,0 +1,216 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'走访报告列表'" :leftFlag="true" :rightFlag="true">
<template #right>
<view class="head-right" @click="handleAdd">
<uni-icons type="plus" size="24" color="#B7D2FF"></uni-icons>新增
</view>
</template>
</customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height" :style="{ paddingTop: navBarPaddingTop + 'px' }"></view>
<!-- 正文内容 -->
<view class="all-body">
<!-- 搜索 @blur="blur" @focus="focus" @input="input" @cancel="cancel" @clear="clear"-->
<view class="search">
<uni-search-bar class="custom-search" radius="28" placeholder="请输入客户名称" clearButton="auto"
cancelButton="none" bgColor="#6FA2F8" textColor="#ffffff"
v-model="searchValue"
/>
<button type="default" @click="handleSearch" size="mini" class="btn-search">查询</button>
</view>
<!-- 分页部分 -->
<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"
:up="upOption" :down="downOption" :fixed="false" textColor="#ffffff" bgColor="#ffffff"
class="scroll-h" :class="{'loading-scroll':cssFlag}"
>
<view class="white-bg margin-bottom20" v-for="(item, index) in list" :key="index" @click="handleDetail(item)">
<view class="report-list">
<view class="title">{{ item.title }}</view>
<view class="r-list">
<view class="r-name">{{ item.name }}</view>
<view class="r-right btn-orange" size="mini">{{ item.statusName }}</view>
</view>
<view class="border-bottom"></view>
<view class="r-list">
<view class="r-left">报告类型</view>
<view class="r-right">{{ item.reportTypeName }}</view>
</view>
<view class="border-bottom"></view>
<view class="r-list">
<view class="r-left">报告人</view>
<view class="r-right">{{ item.reportPeople }}</view>
</view>
<view class="border-bottom"></view>
<view class="r-list">
<view class="r-left">报告日期</view>
<view class="r-right">{{ item.dateStr }}</view>
</view>
</view>
</view>
</mescroll-uni>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
import { getNavBarPaddingTop } from '@/utils/system.js'
import { visitorReportList } from '@/api/business.js'
// 获取导航栏高度用于内容区域padding
const navBarPaddingTop = ref(0);
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop() * 2;
})
let searchValue = ref(null)
// 查询搜索跳转
let handleSearch = () => {
console.log(searchValue.value)
}
// 新增
let handleAdd = ()=>{
uni.navigateTo({ url:'/pages/business/CRM/visitorReportAdd' })
}
// 查询列表
let list = ref([]);
const mescrollRef = ref(null);
const upOption = ref({
page: { num: 0, size: 10 },
noMoreSize: 5,
empty: { tip: '~ 空空如也 ~' },
textLoading: '加载中...',
textNoMore: '已经到底了'
});
const downOption = ref({
auto: true,
textInOffset: '下拉刷新',
textOutOffset: '释放更新',
textLoading: '刷新中...'
});
let cssFlag=ref(false);//控制样式
const mescrollInit = (mescroll) => {
cssFlag.value = true;
mescrollRef.value = mescroll;
};
// 下拉刷新
const downCallback = async (mescroll) => {
try {
setTimeout(async ()=>{
const res = await getVisitorReportList(1, upOption.value.page.size);
cssFlag.value = false;
list.value = res.list;
mescroll.resetUpScroll();
},500);
} catch (error) {
mescroll.endErr();
} finally {
setTimeout(async ()=>{
mescroll.endSuccess();
},500);
}
}
// 上拉加载更多
const upCallback = async (mescroll) => {
try {
setTimeout(async ()=>{
const res = await getVisitorReportList(mescroll.num, mescroll.size);
if (mescroll.num === 1) {
list.value = res.list;
} else {
list.value.push(...res.list);
}
mescroll.endBySize(res.list.length, res.total);
},500);
} catch (error) {
mescroll.endErr();
}
}
// 获取数据列表
const getVisitorReportList = (pageIndex, pageSize) => {
return new Promise(async (resolve) => {
let param = {
pageIndex,
pageSize
}
let res = await visitorReportList(param);
resolve({
list: res.list,
total: res.totalCount
});
});
}
// 跳转到详情
let handleDetail=(item)=>{
uni.navigateTo({
url: "/pages/business/CRM/visitorReportDetail?id="+item.id
})
}
</script>
<style scoped>
.all-body {
/* #ifdef APP-PLUS */
top: 150rpx;
height: calc(100vh - 75px);
/* #endif */
/* #ifndef APP-PLUS */
top:120rpx;
height: calc(100vh);
/* #endif */
}
.search{
display: flex;
}
.search .btn-search{
border:none;
background: none;
line-height: normal;
color:#fff;
line-height: 56rpx !important;
padding:10rpx 0 0;
text-align: left;
cursor: pointer;
}
.search .btn-search::after{
display: none;
}
.search .custom-search{
width:80%;
}
.search .custom-search.uni-searchbar{
padding-right:0 !important;
}
.scroll-h{
/* #ifdef APP-PLUS */
height: calc(100vh - 120px);
/* #endif */
/* #ifndef APP-PLUS */
height: calc(100vh - 110px);
/* #endif */
}
.white-bg{
padding-bottom:10rpx;
}
</style>

View File

@@ -0,0 +1,150 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'走访报告添加'" :leftFlag="true" :rightFlag="true">
<template #right>
<view class="head-right" @click="submitForm">
<uni-icons custom-prefix="iconfont" type="icon-phonebaocun" size="22"
color="#B7D2FF"></uni-icons>保存
</view>
</template>
</customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 正文内容 -->
<view class="white-bg">
<view class="form-con">
<uni-forms ref="formRef" :model="formData" :rules="rules" label-width="100px" >
<uni-forms-item label="客户名称" name="guestName" class="f-c-right">
<picker @change="bindPickerChange" :value="guestIndex" :range="guestArr">
<view class="f-c-text">
{{guestArr[guestIndex]}}
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
</view>
</picker>
</uni-forms-item>
<uni-forms-item label="签到记录" class="f-c-right">
<view class="f-c-text txt-right">--</view>
</uni-forms-item>
<uni-forms-item label="我方领导" name="leader" required class="uni-forms-item is-direction-top is-top">
<uni-easyinput v-model="formData.leader" placeholder="请输入我方领导" />
</uni-forms-item>
<uni-forms-item label="走访日期" name="visitDate" class="uni-forms-item is-direction-top is-top">
<uni-datetime-picker type="date" :clear-icon="false" v-model="formData.visitDate" @change="changeDate" />
</uni-forms-item>
</uni-forms>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import{ getGuestList} from '@/api/business.js'
// 客户名称列表
let guestList = ref([]);
let guestArr = ref([])
let guestIndex = ref(0);
let selectGuestList = async ()=>{
let res = await getGuestList();
let list = res.list;
list.forEach(item => {
guestArr.value.push(item.name)
});
guestList.value = res.list
}
selectGuestList();
// 选择客户列表
let bindPickerChange = (e)=>{
console.log('picker发送选择改变携带值为', e.detail.value)
guestIndex.value = e.detail.value
}
// 表单ref
const formRef = ref(null);
// 表单数据
const formData = ref({
guestName:'',
leader:'',
visitDate:''
});
// 验证规则
const rules = {
leader: {
rules: [
{ required: true, errorMessage: '我方领导不能为空' }
]
}
};
// 修改日期
let changeDate = (e)=>{
console.log('changeDate:', e);
};
// 保存走访报告
const submitForm = async () => {
try {
// 验证表单
await formRef.value.validate();
// 验证通过后的操作
uni.showToast({
title: '验证通过',
icon: 'success'
});
console.log('表单数据:', formData.value);
// 这里可以添加提交到服务器的代码
} catch (err) {
console.log('表单验证失败:', err);
}
};
</script>
<style scoped>
.white-bg {
width: 750rpx;
padding: 30rpx 0 0;
margin-bottom: 0;
border-radius: 8px 8px 0 0;
}
.form-con{
/* #ifdef APP-PLUS */
height:calc(100vh - 100px)
/* #endif */
/* #ifndef APP-PLUS */
height:calc(100vh - 80px)
/* #endif */
}
:deep(.uni-date-x){
display: block;
}
:deep(.uni-date-x .icon-calendar){
float: right;
margin-top:15rpx;
margin-right: 20rpx;
background:url('../../../static/images/business/icon-date.png') no-repeat;
background-size:32rpx 35rpx;
width:32rpx;
height: 35rpx;
}
:deep(.uni-date-x .icon-calendar::before){
display: none;
}
:deep(.uni-date-x .uni-date__x-input){
padding-left:20rpx;
color:#919191;
}
</style>

View File

@@ -0,0 +1,190 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'走访报告详情'" :leftFlag="true" :rightFlag="false"></customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 正文内容 -->
<!-- 详情内容 -->
<view class="white-bg">
<view class="report-list">
<view class="title">{{ item.title }}</view>
<view class="r-list">
<view class="r-name">{{ item.name }}</view>
<view class="r-right btn-pink" size="mini" @click="handleZan">
<uni-icons type="hand-up-filled" size="16" color="#ffffff"></uni-icons>
</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list">
<view class="r-left">报告人</view>
<view class="r-right">{{ item.reportPeople }}</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list">
<view class="r-left">走访日期</view>
<view class="r-right">{{ item.dateStr }}</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list">
<view class="r-left">是否双方高层领导参与</view>
<view class="r-right">{{ item.isJoin }}</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list">
<view class="r-left">活动类型</view>
<view class="r-right">{{ item.activityTypeName }}</view>
</view>
<view class="border-bottom b-width"></view>
</view>
</view>
<!-- 报告明细&评论 -->
<customTabs v-model="activeTab" :tabs="tabList" :modelValue="activeTab">
<!-- 报告明细 -->
<block v-if="activeTab === 0">
<view class="white-bg white-bg-2">
<view class="report-list">
<view class="r-list">
<view class="r-left">拜访事项</view>
<view class="r-right">获得信息</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list">
<view class="r-left">拜访类型</view>
<view class="r-right">竞争对手信息</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list" style="display: block">
<view class="r-name">结果</view>
<view class="r-gray">考察了几家连接器单位陶瓷管壳55所43所现在的开模费有所下降便宜的几千块钱</view>
</view>
</view>
</view>
<view class="white-bg white-bg-2">
<view class="report-list">
<view class="r-list">
<view class="r-left">拜访事项</view>
<view class="r-right">日常走访</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list">
<view class="r-left">拜访类型</view>
<view class="r-right">竞争对手信息</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list">
<view class="r-left">客户人员</view>
<view class="r-right">孙琳琳顾鹏田玲</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list">
<view class="r-left">我方人员</view>
<view class="r-right">肖建华石宪刘启运赵震</view>
</view>
<view class="border-bottom b-width"></view>
<view class="r-list" style="display: block">
<view class="r-name">结果</view>
<view class="r-gray">
<view class="margin-bottom20" >1江苏屹信现在有意向寻找第二家管壳供方主要是因为客户要求做一些封装产品和自研顶目涉及到SIP系统级封装研发工作 </view>
<view>2江苏屹信管壳主要是功放管壳带热沉/CPC/钨铜/钼铜陶瓷管壳希望在30天内交付</view>
</view>
</view>
</view>
</view>
</block>
<!-- 评论 -->
<block v-if="activeTab === 1">
<view class="white-bg white-bg-2">评论内容</view>
</block>
</customTabs>
<!-- 底部加高度来避免tabbar遮挡 -->
<view class="bottom-height"></view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import customHeader from '@/components/customHeader.vue'
import customTabs from '@/components/customTabs.vue';
import { visitorReportDetail } from '@/api/business.js'
// 加载后调用
let id = ref(null)
onLoad((options) => {
id.value = options.id;
getVisitorReportDetail();
})
// 查询详情
let item = ref({});
const getVisitorReportDetail = async () => {
let param = {
id: id.value
}
let res = await visitorReportDetail(param);
item.value = res.activeObj;
}
// 报告明细&评论
const activeTab = ref(0);//默认报告明细
const tabList = ['报告明细', '评论'];
// 跳转走访报告录入
let handleZan = ()=>{
}
</script>
<style scoped>
.white-bg {
width: 690rpx;
margin: 0;
border-radius: 8px 8px 0 0;
}
.white-bg.white-bg-2{
border-radius:0;
margin-bottom:20rpx;
}
:deep(.tabs-header) {
/* background: none !important; */
border-bottom: none !important;
margin: 0 auto;
gap: 5px;
}
:deep(.tab-item) {
color:#919191;
font-size: 32rpx;
font-weight: bold;
/* flex:none; */
/* margin: 0 -50rpx; */
/* padding: 0 50rpx; */
}
:deep(.tab-item:first-child){
text-align: right;
margin-left:230rpx;
}
:deep(.tab-item:last-child){
text-align: left;
margin-right:230rpx;
}
:deep(.tab-item.active) {
color: #3384DF;
font-weight: bold;
}
:deep(.tab-item.active::after) {
width: 100rpx;
height: 8rpx;
}
</style>

View File

@@ -0,0 +1,215 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'走访报告内容录入'" :leftFlag="true" :rightFlag="false"></customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 日常走访 -->
<view class="white-bg white-bg-2">
<view class="w-b-title" @click="handleExpand">日常走访
<text>
{{ expandFlag ? '展开' : '收起' }}
<i :class="{ iconfont: true, 'icon-up': !expandFlag, 'icon-down': expandFlag }"></i>
</text>
</view>
<view v-if="!expandFlag" class="form-con">
<uni-forms ref="formRef" :model="formData" :rules="rules" label-width="100px" label-position="top">
<uni-forms-item label="客户人员" name="guestName1" class="f-c-right">
<multipleSelect :multiple="true" :value="monIndex1" downInner :options="guestList1"
@change="changeValue1" :key="Math.round()"
></multipleSelect>
</uni-forms-item>
<uni-forms-item label="我方参与人" name="guestName2" class="f-c-right">
<multipleSelect :multiple="true" :value="monIndex2" downInner :options="guestList2"
@change="changeValue2" :key="Math.round()"
></multipleSelect>
</uni-forms-item>
<uni-forms-item label="活动内容" name="activeCon" required
class="uni-forms-item is-direction-top is-top">
<multipleSelect :multiple="true" :value="monIndex3" downInner :options="guestList3"
@change="changeValue3" :key="Math.round()"
></multipleSelect>
</uni-forms-item>
<uni-forms-item label="活动文字" name="activeTxt" class="uni-forms-item is-direction-top is-top">
<uni-easyinput type="textarea" autoHeight v-model="formData.activeTxt" placeholder="请输入" class="form-texarea" />
</uni-forms-item>
</uni-forms>
<view class="footer-con">
<button class="btn-default" type="default" @click="handleDelete" size="mini"> </button>
<button class="btn-primary" type="primary" @click="submitForm" size="mini">保存/修改</button>
</view>
</view>
</view>
<!-- 业务招待 -->
<view class="white-bg white-bg-2 white-bg-3">
<view class="w-b-title" @click="handleExpand2">业务招待
<text>
{{ expandFlag2 ? '展开' : '收起' }}
<i :class="{ iconfont: true, 'icon-up': !expandFlag2, 'icon-down': expandFlag2 }"></i>
</text>
</view>
<view v-if="!expandFlag2" class="form-con">
业务招待
</view>
</view>
<!-- 技术交流 -->
<view class="white-bg white-bg-2 white-bg-3">
<view class="w-b-title" @click="handleExpand3">技术交流
<text>
{{ expandFlag3 ? '展开' : '收起' }}
<i :class="{ iconfont: true, 'icon-up': !expandFlag3, 'icon-down': expandFlag3 }"></i>
</text>
</view>
<view v-if="!expandFlag3" class="form-con">
技术交流
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import multipleSelect from '@/components/multipleSelect.vue'
// 日常走访
let expandFlag = ref(false);
let handleExpand = () => {
expandFlag.value = !expandFlag.value;
}
// 客户人员
let monIndex1= reactive([0]);
const changeValue1 = (item, value) => {
console.log("客户人员", item, value);
monIndex1 = value;
};
const guestList1 = [
{ value: 0, text: "客户1" },
{ value: 1, text: "客户2" },
{ value: 2, text: "客户3" },
{ value: 3, text: "客户4" },
{ value: 4, text: "客户5" },
];
// 我方人员
let monIndex2= reactive([0]);
const changeValue2 = (item, value) => {
console.log("我方人员", item, value);
monIndex2 = value;
};
const guestList2 = [
{ value: 0, text: "我方1" },
{ value: 1, text: "我方2" },
{ value: 2, text: "我方3" },
{ value: 3, text: "我方4" },
{ value: 4, text: "我方5" },
];
// 活动内容
let monIndex3= reactive([0]);
const changeValue3 = (item, value) => {
console.log("活动内容", item, value);
monIndex2 = value;
};
const guestList3 = [
{ value: 0, text: "活动内容1" },
{ value: 1, text: "活动内容2" },
{ value: 2, text: "活动内容3" },
{ value: 3, text: "活动内容4" },
{ value: 4, text: "活动内容5" },
];
// 业务招待
let expandFlag2 = ref(true);
let handleExpand2 = () => {
expandFlag2.value = !expandFlag2.value;
}
// 技术交流
let expandFlag3 = ref(true);
let handleExpand3 = () => {
expandFlag3.value = !expandFlag3.value;
}
// 表单ref
const formRef = ref(null);
// 表单数据
const formData = ref({
guestName1: '',
guestName2: '',
activeCon:'',
activeTxt: ''
});
// 验证规则
const rules = {
activeCon: {
rules: [
{ required: true, errorMessage: '请选择活动内容' }
]
}
};
// 删除
let handleDelete = ()=>{
}
// 保存/修改
const submitForm = async () => {
try {
// 验证表单
await formRef.value.validate();
// 验证通过后的操作
uni.showToast({
title: '验证通过',
icon: 'success'
});
console.log('表单数据:', formData.value);
// 这里可以添加提交到服务器的代码
} catch (err) {
console.log('表单验证失败:', err);
}
};
</script>
<style scoped>
.white-bg {
width: 690rpx;
margin: 0;
border-radius: 8px 8px 0 0;
}
.white-bg.white-bg-2 {
margin-bottom: 20rpx;
}
.white-bg.white-bg-3 {
border-radius:0
}
.white-bg .w-b-title {
color: #3384DF;
font-size: 38rpx;
}
.form-con{
padding:30rpx 0 0;
}
:deep(.form-con .uni-forms-item){
margin-bottom:22px !important;
}
</style>

View File

@@ -0,0 +1,185 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'签到打卡'" :leftFlag="true" :rightFlag="false"></customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 正文内容 -->
<view class="white-bg">
<image src="../../../static/images/business/btn-qd.png" class="btn-image" @click="handleCheckIn" />
<image src="../../../static/images/business/btn-dk.png" class="btn-image" @click="handleClockIn" />
<view class="check-desc">
业务人员可通过<text class="font-orange">签到</text><text class="font-blue">打卡</text>进行行为记录该时间会和走访报告中的时间进行关联便于查看
</view>
</view>
</view>
<!-- 打卡遮罩层 -->
<view class="check-con" v-if="checkFlag">
<view class="check-in">
<view class="check-tip">打卡</view>
<view class="check-title">确定要在此处打卡吗</view>
<view class="check-location">
<uni-icons type="location-filled" size="30" color="#0395E0"></uni-icons> 亚洲金融大厦
</view>
<view class="check-address">北京市朝阳区天辰东路1号院</view>
<view class="check-footer">
<button class="btn-default" type="default" @click="handleCancel" size="mini"> </button>
<button class="btn-primary" type="primary" @click="handleSubmit" size="mini"> </button>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
let checkFlag = ref(false);
// 签到
let handleCheckIn = () => {
console.log("签到")
checkFlag.value = true;
}
// 打卡
let handleClockIn = () => {
console.log("打卡")
checkFlag.value = true;
}
// 取消
let handleCancel = () => {
checkFlag.value = false;
}
// 确定
let handleSubmit = () => {
checkFlag.value = false;
}
</script>
<style scoped>
.white-bg {
width: 650rpx;
margin: 0;
border-radius: 8px 8px 0 0;
padding: 50rpx;
/* #ifdef APP-PLUS */
height: calc(100vh - 125px);
/* #endif */
/* #ifndef APP-PLUS */
height: calc(100vh - 98px);
/* #endif */
}
.btn-image {
width: 340rpx;
height: 340rpx;
margin: 30rpx auto 60rpx;
display: block;
}
.check-desc {
background-color: #F5F5F5;
padding: 40rpx 50rpx;
font-size: 28rpx;
border-radius: 10px;
margin-top: 100rpx;
}
.check-desc .font-orange {
color: #F5813A;
font-size: 32rpx;
font-weight: bold;
}
.check-desc .font-blue {
color: #2CBAEF;
font-size: 32rpx;
font-weight: bold;
}
/* 弹窗处理 */
.check-con {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
}
/* 遮罩层内容样式 */
.check-in {
background-color: #fff;
padding: 40rpx 30rpx 60rpx;
border-radius: 10px;
/* width: 620rpx; */
width: 560rpx;
position: absolute;
top: 48%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.check-in .check-tip {
font-weight: bold;
}
.check-in .check-title {
font-size: 32rpx;
padding: 20rpx 0;
}
.check-in .check-location{
color:#0395E0;
font-size:42rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
margin:30rpx 0;
}
.check-in .check-location .uniui-location-filled{
font-weight: normal;
margin-right: 20rpx;
}
.check-in .check-address{
color:#919191;
font-size: 32rpx;
margin-bottom:80rpx;
}
.check-in .check-footer {
display: flex;
}
.check-in .check-footer .btn-default,
.check-in .check-footer .btn-primary {
background-color: #fff;
border: 1px solid #05A3F4;
color: #05A3F4;
border-radius: 25px;
padding: 0rpx 80rpx;
font-size: 34rpx;
/* margin-left: 0;
margin-right: 20rpx; */
}
.check-in .check-footer .btn-primary {
background-color: #05A3F4;
border: 1px solid #05A3F4;
color: #fff;
/* padding: 0rpx 60rpx; */
}
</style>

View File

@@ -0,0 +1,142 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'修改周计划'" :leftFlag="true" :rightFlag="true">
<template #right>
<view class="head-right" @click="submitForm">
<uni-icons custom-prefix="iconfont" type="icon-phonebaocun" size="22"
color="#B7D2FF"></uni-icons>保存
</view>
</template>
</customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 正文内容 -->
<view class="week-plan-title">
<view>姓名赵欣鸣</view>
<view>2025-09 3 </view>
</view>
<view class="white-bg white-bg-2">
<view class="w-b-title">2025-09-21 星期一
<text>
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
</text>
</view>
<view class="form-con">
<uni-forms ref="formRef" :model="formData" :rules="rules" label-width="100px" label-position="top">
<uni-forms-item label="工作类型" name="workType">
<view class="form-picker">
<picker @change="changeValue" :value="workIndex" :range="workList">
<view class="flex">
{{workList[workIndex]}}
<uni-icons type="down" size="20" color="#A0A0A0"></uni-icons>
</view>
</picker>
</view>
</uni-forms-item>
<uni-forms-item label="内容" name="content">
<uni-easyinput type="textarea" autoHeight v-model="formData.content" placeholder="请输入" class="form-texarea" />
</uni-forms-item>
</uni-forms>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import multipleSelect from '@/components/multipleSelect.vue'
// 工作类型
let workIndex= ref(0);
const workList = ["外出","出差","公司","办事处"];
// 表单ref
const formRef = ref(null);
// 表单数据
const formData = ref({
workType:'',
content: ''
});
// 验证规则
const rules = {
workType: {
rules: [
{ required: true, errorMessage: '请选择工作类型' }
]
}
};
// 选择工作类型
const changeValue = (e) => {
console.log("工作类型", e.detail.value);
workIndex = e.detail.value;
formData.value.workType = workList[workIndex]
};
// 保存
const submitForm = async () => {
try {
// 验证表单
await formRef.value.validate();
// 验证通过后的操作
uni.showToast({
title: '验证通过',
icon: 'success'
});
console.log('表单数据:', formData.value);
// 这里可以添加提交到服务器的代码
} catch (err) {
console.log('表单验证失败:', err);
}
};
</script>
<style scoped>
.week-plan-title{
color:#fff;
padding: 0 30rpx 30rpx;
display: flex;
justify-content: space-between;
}
.white-bg {
width: 690rpx;
margin: 0;
border-radius: 8px 8px 0 0;
}
.white-bg.white-bg-2 {
margin-bottom: 20rpx;
}
.white-bg.white-bg-3 {
border-radius:0
}
.white-bg .w-b-title {
color: #3384DF;
font-size: 38rpx;
}
.form-con{
padding:30rpx 0 0;
}
:deep(.form-con .uni-forms-item){
margin-bottom:22px !important;
}
</style>

View File

@@ -0,0 +1,181 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'业务中心'"
:leftFlag="false" :rightFlag="false"
></customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 搜索 -->
<view class="search">
<uni-search-bar class="custom-search" radius="28"
placeholder="请输入您想查询的内容或服务"
clearButton="auto" cancelButton="none"
bgColor="#6FA2F8" textColor="#ffffff"
@confirm="handleSearch"
/>
</view>
<!-- 下拉刷新 -->
<mescroll-uni ref="mescrollRef" @init="mescrollInit"
:down="downOption" @down="downCallback"
:fixed="false" class="scroll-h"
>
<!-- 首页日常服务 -->
<view class="white-bg">
<view class="w-b-title">
首页日常服务
<view type="primary" @click="handleEdit" class="btn-edit"> </view>
</view>
</view>
<!-- 企业日常 -->
<view class="white-bg">
<view class="w-b-title" @click="handleExpand">企业日常
<text>{{expandFlag?'展开':'收起'}}<i :class="{iconfont:true,'icon-up':!expandFlag,'icon-down':expandFlag}"></i></text>
</view>
<view class="logo-list" v-if="!expandFlag">
<view v-for="(item,index) in list1" class="l-l-item" :key="index">
<img :src="item.imgSrc" />
<text class="font-gray">{{ item.name }}</text>
</view>
</view>
</view>
<!-- CRM系统 -->
<view class="white-bg">
<view class="w-b-title" @click="handleExpand2">CRM系统
<text>{{expandFlag2?'展开':'收起'}}<i :class="{iconfont:true,'icon-up':!expandFlag2,'icon-down':expandFlag2}"></i></text>
</view>
<view class="logo-list" v-if="!expandFlag2">
<view v-for="(item,index) in list2" class="l-l-item" :key="index" @click="handleJump(item.url)">
<uni-badge :text="item.badgeCount" size="small"></uni-badge>
<img :src="item.imgSrc" />
<text class="font-gray">{{ item.name }}</text>
</view>
</view>
</view>
<!-- 供应链采纳 -->
<view class="white-bg">
<view class="w-b-title">供应链采纳
<text>展开<i :class="{iconfont:true,'icon-down':true}"></i></text>
</view>
</view>
<!-- PLM系统 -->
<view class="white-bg">
<view class="w-b-title">PLM系统
<text>展开<i :class="{iconfont:true,'icon-down':true}"></i></text>
</view>
</view>
<!-- 底部加高度来避免tabbar遮挡 -->
<view class="bottom-height"></view>
</mescroll-uni>
</view>
</view>
</template>
<script setup>
import { ref,onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
import { getNavBarPaddingTop} from '@/utils/system.js'
import { businessDaily,businessCRMList } from '@/api/business.js';
// 获取导航栏高度用于内容区域padding
const navBarPaddingTop = ref(0);
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop()*2;
})
// 查询搜索跳转
let handleSearch = ()=>{
}
// 下拉刷新
const mescrollRef = ref(null);
const mescrollInit = (mescroll) => {
mescrollRef.value = mescroll;
};
const downOption = ref({
auto: true,
textInOffset: '下拉刷新',
textOutOffset: '释放更新',
textLoading: '刷新中...'
});
// 下拉刷新
const downCallback = async (mescroll) => {
try {
setTimeout(async ()=>{
// mescroll.resetUpScroll();
},500);
} catch (error) {
mescroll.endErr();
} finally {
setTimeout(async ()=>{
mescroll.endSuccess();
},500);
}
}
// 日常服务编辑
let handleEdit=()=>{
}
// 图标查询处理
// 1.企业日常
let list1 = ref([])
let getBusinessDailyList= async ()=>{
let busRes = await businessDaily({});
list1.value = busRes.list || [];
}
getBusinessDailyList();
// 企业日常-右侧展开
let expandFlag=ref(false);
let handleExpand = ()=>{
expandFlag.value = !expandFlag.value;
}
// 2.CRM系统
let list2 = ref([])
let getBusinessCRMList= async ()=>{
let busRes = await businessCRMList({});
list2.value = busRes.list || [];
}
getBusinessCRMList();
// 企业日常-右侧展开
let expandFlag2=ref(false);
let handleExpand2 = ()=>{
expandFlag2.value = !expandFlag2.value;
}
// 跳转
let handleJump=(url)=>{
console.log(url)
if(url){
uni.navigateTo({ url })
}
}
</script>
<style scope>
.scroll-h{
/* #ifdef APP-PLUS */
height: calc(100vh - 120px) !important;
/* #endif */
/* #ifndef APP-PLUS */
height: calc(100vh - 140px) !important;
/* #endif */
}
:deep(.mescroll-upwarp){
display:none
}
</style>

430
src/pages/home/home.vue Normal file
View File

@@ -0,0 +1,430 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 下拉刷新 -->
<mescroll-uni ref="mescrollRef" @init="mescrollInit"
:down="downOption" @down="downCallback"
:fixed="false" class="scroll-h" :style="{ paddingTop: navBarPaddingTop + 'px' }"
>
<!-- #ifdef H5 -->
<view style="height:50rpx"></view>
<!-- #endif -->
<!-- 搜索 -->
<view class="search search-sao" >
<uni-search-bar class="custom-search" radius="28"
placeholder="请输入您想查询的内容或服务"
clearButton="auto" cancelButton="none"
bgColor="#6FA2F8" textColor="#ffffff"
@confirm="handleSearch"
/>
<uni-icons custom-prefix="iconfont" color="#ffffff" type="icon-phonesaoyisao" size="20"></uni-icons>
</view>
<!-- 待办内容 -->
<view class="backlog-bg">
<view class="backlog-b-item">
<view class="font-number">{{ backBlogObj.count1 }}</view>
<view class="font-title">待办</view>
</view>
<view class="backlog-b-item">
<view class="font-number">{{ backBlogObj.count2 }}</view>
<view class="font-title">待审查</view>
</view>
<view class="backlog-b-item">
<view class="font-number">{{ backBlogObj.count3 }}</view>
<view class="font-title">待巡检</view>
</view>
<view class="backlog-b-item">
<view class="font-number">{{ backBlogObj.count4 }}</view>
<view class="font-title">待发货</view>
</view>
</view>
<!-- 跑马灯滚动 -->
<view class="notice-bg">
<img :src="'static/images/icon-notice@2x.png'" class="notice-icon" />
<view class="notice-list">
<!-- :interval="4000" -->
<swiper class="swiper-con"
:vertical="true"
:autoplay="true"
:duration="500"
:circular="true"
:disable-touch="true"
:display-multiple-items="1"
>
<swiper-item v-for="(item, index) in extendedList" :key="index" >
<view class="swiper-item">
{{ item }}
</view>
</swiper-item>
</swiper>
</view>
</view>
<!-- 日程提醒 -->
<view class="white-bg mar-top" v-if="stepList.length>0">
<view class="w-b-title">日程提醒
<view class="yellow-bg">
<i :class="{iconfont:true,'icon-phoneshizhong':true}"></i>
<view class="text-black">{{ weekStr }}</view>
</view>
</view>
<view class="section-line">
<customSteps :steps="stepList" :modelValue="stepList"></customSteps>
</view>
</view>
<!-- 销售任务完成情况 -->
<view class="white-bg mar-top">
<view class="w-b-title">销售任务完成情况
<view class="yellow-bg">
<picker @change="bindPickerChange" :value="activeIndex" :range="salesList" @click="clickPicker" @cancel="bindPickerCancel">
<view class="uni-input">{{salesList[activeIndex]}}</view>
</picker>
<i :class="{iconfont:true,'icon-down':salesFlag,'icon-up':!salesFlag}" class="picker-icon"></i>
</view>
</view>
<view class="progress-bg">
<progress :percent="percentNum" stroke-width="10" activeColor="#41E1B1" backgroundColor="#F0F0F0" />
<view class="percent" :style="{left:percentNum+'%'}">
<view class="percent-num">{{ percentNum }}%</view>
<i class="iconfont icon-down"></i>
</view>
<view class="percent-con">
<view class="p-first">
<view>实际销售额</view>
<view class="font-money">{{totalSales}}</view>
</view>
<view class="p-last">
<view>目标销售额</view>
<view class="font-money">{{ targetSales }}</view>
</view>
</view>
</view>
</view>
<!-- 常用服务 -->
<view class="white-bg">
<view class="w-b-title">常用服务</view>
<view class="logo-list">
<view v-for="(item,index) in commonServiceList" class="l-l-item" :key="index">
<img :src="item.imgSrc" />
<text class="font-gray">{{ item.name }}</text>
</view>
<!-- <view class="l-l-item" @click="handleAddCommonSercice">
<img :src="'static/images/business/icon-add.png'">
<text class="font-gray">添加</text>
</view> -->
</view>
</view>
<!-- 新闻公告 -->
<view class="white-bg">
<view class="w-b-title">新闻公告
<text>更多新闻</text>
</view>
<view class="news-list">
<view v-for="(item,index) in newsList" class="news-item" :key="index">
<view class="n-i-title">{{ item.name }}
<view class="n-i-date">{{ formatDateStr(item.date) }}</view>
</view>
<img :src="item.imgSrc" v-if="item.imgSrc" />
</view>
</view>
</view>
<!-- 底部加高度来避免tabbar遮挡 -->
<view class="bottom-height"></view>
</mescroll-uni>
</view>
</view>
</template>
<script setup>
import { ref,onMounted,computed } from 'vue'
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
import customSteps from '@/components/customSteps.vue'
import { getNavBarPaddingTop} from '@/utils/system.js'
import { backBlogCount,swiperList,stepData,salesTask,commonServices,newsQueryList } from '@/api/home.js';
import { getWeekStr,formatTimestamp } from '@/utils/datetime.js'
import { formatMoney } from '@/utils/formatter.js'
// 下拉刷新
const mescrollRef = ref(null);
const mescrollInit = (mescroll) => {
mescrollRef.value = mescroll;
};
const downOption = ref({
auto: true,
textInOffset: '下拉刷新',
textOutOffset: '释放更新',
textLoading: '刷新中...'
});
// 下拉刷新
const downCallback = async (mescroll) => {
try {
setTimeout(async ()=>{
// mescroll.resetUpScroll();
},500);
} catch (error) {
mescroll.endErr();
} finally {
setTimeout(async ()=>{
mescroll.endSuccess();
},500);
}
}
// 获取导航栏高度用于内容区域padding
const navBarPaddingTop = ref(0);
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop()*2;
})
// 查询搜索跳转
let handleSearch = ()=>{
}
// tabar 增加消息数量
try {
wx.setTabBarBadge({
index: 2, // TabBar的索引从0开始计数
text: '3' // 徽章的文本
});
} catch (error) {
console.error('设置TabBar Badge失败:', error);
}
let backBlogObj = ref({})
// 获取待办数据
let getBackBlogCount = async () =>{
backBlogObj.value = await backBlogCount();
}
getBackBlogCount();
// 跑马灯处理
let extendedList = ref([]);// ;
const getSwiperList = async () => {
extendedList.value = await swiperList();
}
getSwiperList();
// 日程提醒
let weekStr = ref(null) //'2025-09-19 星期三'
let stepList = ref([]);
const getStepData = async ()=>{
let res = await stepData({});
weekStr.value = getWeekStr(res.date);
stepList.value = res.list;
}
getStepData();
// 销售任务完成情况
let salesList = ref([]);
let salesFlag = ref(true);
let activeIndex = ref(0);
const bindPickerChange = (e)=>{
// console.log('picker发送选择改变携带值为', e.detail.value)
activeIndex.value = e.detail.value;
salesFlag.value = true;
}
const bindPickerCancel= (e)=>{
salesFlag.value = true;
}
const clickPicker= (e)=>{
salesFlag.value = false;
}
let percentNum = ref(0);
let totalSales = ref(0)
let targetSales = ref(0)
const getSalesTask = async ()=>{
let res = await salesTask({});
salesList.value = res.salesList;
percentNum.value = res.percentNum;
totalSales.value = formatMoney(res.totalSales);
targetSales.value = formatMoney(res.targetSales);
}
getSalesTask();
// 常用服务
let commonServiceList = ref([])
const getCommonServices = async ()=>{
let res = await commonServices({});
commonServiceList.value = res.list
}
getCommonServices();
// 添加常用服务
const handleAddCommonSercice = ()=>{
}
// 新闻公告
let newsList = ref([])
const getNewsList = async()=>{
let res = await newsQueryList({});
newsList.value = res.list;
}
getNewsList();
const formatDateStr =(times)=>{
return formatTimestamp(times)
}
</script>
<style scope>
.scroll-h{
/* #ifdef APP-PLUS */
height:calc(100vh - 30px) !important;
/* #endif */
/* #ifndef APP-PLUS */
height: calc(100vh - 30px) !important;
/* #endif */
}
:deep(.mescroll-upwarp){
display:none
}
.search-sao{
display: flex;
padding-top:10rpx;
}
.search-sao .custom-search{
width:612rpx;
}
.search-sao :deep(.custom-search.uni-searchbar){
padding-bottom: 15rpx !important;
}
.search-sao .icon-phonesaoyisao{
margin:15rpx 30rpx 10rpx auto;
}
.backlog-bg{
background:url('@/static/images/bg-main@2x.png') no-repeat;
background-size:730rpx 205rpx;
/* width:730rpx; */
width:690rpx;
height:205rpx;
margin:0 auto;
display: flex;
padding:0 20rpx;
}
.backlog-b-item{
width:25%;
color:#579FF9;
border-right:1px solid #EAEAEA;
height:100rpx;
margin-top:40rpx;
}
.backlog-b-item .font-number{
font-size: 60rpx;
font-weight: bold;
text-align: center;
}
.backlog-b-item .font-title{
font-size:28rpx;
text-align: center;
}
.notice-bg{
background:url('@/static/images/bg-notice@2x.png') no-repeat;
background-size:750rpx 90rpx;
width:690rpx;
height: 90rpx;
margin-top:10rpx;
padding:0 30rpx;
display: flex;
align-items: center;
}
.notice-bg .notice-icon{
width:46rpx;
height:40rpx;
}
.notice-list{
width:100%;
position: relative;
height: 90rpx;
overflow: hidden;
}
.notice-list .swiper-con{
width:640rpx;
}
.notice-list .swiper-con .swiper-item {
height: 90rpx;
line-height: 90rpx;
padding: 0 20rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color:#fff;
font-size:30rpx;
}
.progress-bg{
position: relative;
width:528rpx;
margin:120rpx auto 0;
}
.progress-bg :deep(.uni-progress .uni-progress-bar){
border-radius: 5px;
}
.progress-bg :deep(.uni-progress .uni-progress-inner-bar){
border-radius: 5px 0 0 5px;
}
.progress-bg .percent{
position: absolute;
top:0;
color:#FF687A;
font-size:60rpx;
font-weight: bold;
}
.progress-bg .percent .icon-down{
position: absolute;
bottom:0rpx;
left:-20rpx;
color:#FF687A;
font-size:40rpx;
}
.progress-bg .percent .percent-num{
position: absolute;
left:-60rpx;
bottom:30rpx;
}
.progress-bg .percent-con{
display: flex;
width:528rpx;
color:#333;
font-size:28rpx;
margin:20rpx 0 10rpx;
}
.progress-bg .percent-con .p-last{
margin-left: auto;
text-align: right;
}
.progress-bg .percent-con .font-money{
font-weight: bold;
margin-top:10rpx;
}
.white-bg .logo-list{
gap:50rpx;
padding:0 20rpx;
/* margin-bottom: -50rpx; */
}
.white-bg .logo-list .l-l-item{
width:160rpx;
margin-bottom:0rpx;
}
.white-bg .logo-list .l-l-item img{
width:110rpx;
height:110rpx;
}
</style>

View File

@@ -0,0 +1,86 @@
<template>
<view>
<view class="container" :style="{ height: `100vh` }">
<view class="bg"></view>
<view class="version">Version {{ version }}</view>
<view class="bottom-bg"></view>
</view>
</view>
</template>
<script setup>
import { onLoad } from '@dcloudio/uni-app';
import { useUserStore } from '@/stores/user';
const userStore = useUserStore()
const version="1.0.0"
onLoad((opt) => {
console.log("onLoad");
// 检查是否已登录 并 获取用户信息
if (userStore.isLogin) {
// userStore.getUser()
// TODO 未登录会在拦截器中处理跳转登录页, 请在 xxx 配置登录页路径
// #ifdef H5
window.setTimeout(()=>{
uni.reLaunch({
url: '/pages/home/home',
});
},1000)
// #endif
// #ifdef APP-PLUS
uni.reLaunch({
url: '/pages/home/home',
});
// #endif
}else{
// #ifdef H5
window.setTimeout(()=>{
uni.reLaunch({
url: '/pages/login/login',
});
},1000)
// #endif
// #ifdef APP-PLUS
uni.reLaunch({
url: '/pages/login/login',
});
// #endif
}
});
</script>
<style>
.container {
background:#307AF5 !important;
height:100vh !important;
position: relative;
}
.container .bg{
background:url('@/static/images/loading-logo.png') no-repeat;
background-size:700rpx 800rpx;
width: 700rpx;
height: 800rpx;
margin:0 auto;
}
.container .version{
color:#A8D4FF;
font-size:32rpx;
text-align: center;
margin-top:35rpx;
}
.container .bottom-bg{
background:url('@/static/images/loading-txt.png') no-repeat;
background-size:656rpx 123rpx;
width: 656rpx;
height: 123rpx;
position: absolute;
bottom:48rpx;
left:50%;
margin-left:-328rpx;
}
</style>

413
src/pages/login/login.vue Normal file
View File

@@ -0,0 +1,413 @@
<template>
<view class="container">
<view class="login-con">
<view :style="{height: navBarPaddingTop + 'px'}"></view>
<image mode="aspectFit" src="../../static/images/pic-logo.png" class="login-logo"></image>
<view class="login-title">欢迎来到718友晟</view>
<view class="login-tab">
<customTabs v-model="activeTab" :tabs="tabList" :modelValue="activeTab">
<!-- 验证码登录 -->
<view v-show="activeTab === 0">
<view class="login-form">
<uni-forms ref="form" :model="formData" :rules="rules" label-position="top">
<uni-forms-item label="手机号" required name="phone">
<view class="code-con">
<uni-icons custom-prefix="iconfont" color="#239FDF" type="icon-phone" size="20"></uni-icons>
<uni-easyinput type="number" :inputBorder="false"
v-model="formData.phone" placeholder="请输入手机号"
maxlength="11"
/>
</view>
</uni-forms-item>
<uni-forms-item label="验证码" required name="verifyCode">
<view class="code-con">
<uni-icons custom-prefix="iconfont" color="#239FDF" type="icon-code" size="20"></uni-icons>
<uni-easyinput type="number" :inputBorder="false"
v-model="formData.verifyCode" placeholder="请输入验证码"
maxlength="6" style="width:190rpx"
/>
<button type="primary" plain @click="getCode" size="mini"
:loading="codeLoading" :disabled="codeDisabled" class="btn-plain"
>{{codeText}}</button>
</view>
</uni-forms-item>
</uni-forms>
</view>
</view>
<!-- 账号登录 -->
<view v-show="activeTab === 1">
<view class="login-form">
<uni-forms ref="form2" :model="formData2" :rules="rules2" label-position="top">
<uni-forms-item label="用户名" required name="username">
<view class="code-con">
<uni-easyinput prefixIcon="person" :inputBorder="false"
v-model="formData2.username" placeholder="请输入用户名"
/>
</view>
</uni-forms-item>
<uni-forms-item label="密码" required name="password" class="password">
<view class="code-con">
<uni-easyinput prefixIcon="locked" type="password" :inputBorder="false"
v-model="formData2.password" placeholder="请输入登录密码"
/>
</view>
</uni-forms-item>
</uni-forms>
</view>
</view>
</customTabs>
<view class="agreen-con">
<uni-icons v-if="agreeChecked" type="checkbox-filled" color="#02C74C" size="25" @click="agreeCheck"></uni-icons>
<uni-icons v-else type="circle" color="#bfbfbf" size="25" @click="agreeCheck"></uni-icons>
<view class="agreen-xy">
我已阅读并接受<text @click="agreeVisable(1)">服务条款</text><text @click="agreeVisable(2)">隐私保护协议</text>
</view>
</view>
<button type="primary" class="btn-submit" @click="submitForm" :loading="btnLoading" :disabled="btnLoading"> </button>
</view>
</view>
<view class="login-bottom"></view>
</view>
</template>
<script setup>
import { ref,onMounted } from 'vue';
import customTabs from '@/components/customTabs.vue';
import {isPhoneNumber} from '@/utils/validate';
import {showAlert} from '@/utils/message';
import { getCaptchaImage,getVerifyCode,login } from '@/api/auth';
import cache from '@/utils/cache';
import { AGREEWELCOME_KEY } from '@/enums/cacheEnums';
import { getNavBarPaddingTop} from '@/utils/system.js'
import { useUserStore } from '@/stores/user';
const userStore = useUserStore()
// 获取导航栏高度用于内容区域padding
const navBarPaddingTop = ref(0);
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop();
})
const activeTab = ref(1);//默认账号登录
const tabList = ['验证码登录', '账号登录'];
// 验证码登录
const form = ref(null);
const formData = ref({
phone: '15112345600',
verifyCode: '123456',
loginType:0
});
const rules = {
phone: {
rules: [
{ required: true, errorMessage: '请输入手机号' },
{
validateFunction:function(rule,value,data,callback){
if (!isPhoneNumber(value)) {
callback('请正确输入手机号')
}
return true
}
}
]
},
verifyCode: {
rules: [
{ required: true, errorMessage: '请输入验证码' },
{ pattern: /^[0-9]{6}$/, errorMessage: '验证码必须是6位数字' }
]
}
};
let codeLoading = ref(false);
let codeDisabled = ref(false);
let codeText = ref('发送验证码');
let countdown = ref(60);
let timer = ref(null);
let backCode=ref(null)
// 验证特定字段
const validateField = async (fieldName) => {
try {
await form.value.validateField(fieldName);
return true;
} catch (e) {
return false;
}
};
// 发送验证码
let getCode=async()=> {
// 1.验证手机号
const isValid = await validateField('phone');
if (isValid) {
codeLoading.value = true;
codeDisabled.value = true;
// 2.调用获取短信验证码接口
let res = await getVerifyCode({phone:formData.value.phone})
backCode.value = res.code;
codeText.value = `发送中...`;
// 3.开始倒计时
startCountdown();
}
// showAlert("验证码已发送!",'提示',false);
}
let startCountdown=()=>{
if (timer.value) return;
timer.value = setInterval(() => {
if (countdown.value > 0) {
countdown.value--;
codeText.value = `${countdown.value}`;
} else {
resetCountdown();
}
codeLoading.value = false;
}, 1000);
}
let resetCountdown =()=>{
clearInterval(timer.value);
timer.value = null;
countdown.value = 60;
codeText.value = '发送验证码';
codeDisabled.value = false;
}
// 账号登录
const form2 = ref(null);
const formData2 = ref({
username: 'admin',
password: '123456',
loginType:1
});
const rules2 = {
username: {
rules: [
{ required: true, errorMessage: '请输入用户名' },
{ minLength: 3, maxLength: 10, errorMessage: '用户名长度在3到10个字符之间' }
]
},
password: {
rules: [
{ required: true, errorMessage: '请输入密码' },
{ pattern: /^[a-zA-Z0-9]{6,12}$/, errorMessage: '密码必须是6-12位字母或数字' }
]
}
};
// 协议
let agreeChecked = ref(true);
// 勾选同意协议 存入缓存
const agreeCheck = ()=>{
agreeChecked.value = !agreeChecked.value;
if (agreeChecked.value) {
cache.set(AGREEWELCOME_KEY, agreeChecked.value)
} else {
cache.remove(AGREEWELCOME_KEY);
}
}
const btnLoading=ref(false)
// 登录提交
const submitForm = () => {
btnLoading.value = true;
console.log("submitForm=>activeTab=>",activeTab.value)
// 手机获取验证码登录
if(activeTab.value===0){
form.value.validate().then(async param => {
// 2.调用登录接口
param.loginType = activeTab.value; console.log('表单数据00:', param);
let res = await login(param)
// 3.登录后存储token
userStore.login(res);
uni.switchTab({ url: '/pages/home/home' })
btnLoading.value = false;
}).catch(err => {
console.log('表单错误00:', err);
btnLoading.value = false;
});
}else if(activeTab.value===1){
// 用户名和密码登录
form2.value.validate().then(async param => {
param.loginType = activeTab.value; console.log('表单数据11:', formData2.value);
let res = await login(param);
userStore.login(res);
uni.switchTab({ url: '/pages/home/home' })
btnLoading.value = false;
}).catch(err => {
console.log('表单错误11:', err);
btnLoading.value = false;
});
}
};
</script>
<style scoped>
.container {
height: 100vh;
}
.login-con {
position: relative;
background: url('@/static/images/login-bg.png') no-repeat;
background-size: 750rpx 633rpx;
width: 750rpx;
height: 633rpx;
margin:0 auto;
}
.login-con .login-logo {
width: 109rpx;
height: 91rpx;
margin: 84rpx 0 0 70rpx;
}
.login-con .login-title {
font-size: 56rpx;
color: #fff;
font-weight: bold;
margin: 0 0 35rpx 70rpx;
}
:deep(.login-tab .tabs-header) {
background: none !important;
border-bottom: none !important;
width: 438rpx;
margin: 0 auto;
}
:deep(.login-tab .tab-item) {
color: rgba(255, 255, 255, 0.5);
font-size: 36rpx;
font-weight: bold;
}
:deep(.tab-item.active) {
color: #fff;
}
:deep(.tab-item.active::after) {
background-color: #fff;
width: 100rpx;
height: 8rpx;
/* display: none; */
}
.login-form {
background: #fff;
border-radius: 30rpx;
padding: 30rpx 50rpx 15rpx;
width:510rpx;
margin: 20rpx auto 0;
box-shadow: 0rpx -1rpx 16rpx 0rpx rgba(0, 0, 0, 0.1);
}
.code-con{
padding:0 0 10rpx;
margin:0 auto;
display: flex;
align-items: center;
color:#919191;
border-bottom: 1px solid #E7E7E7;
}
:deep(.uni-forms-item__label){
color:#239FDF;
font-weight: bold;
font-size:28rpx;
padding:0 !important;
}
:deep(.uni-easyinput__content){
color:#333;
font-weight: bold;
font-size:32rpx;
}
:deep(.uni-easyinput__content) .uni-icons{
font-weight: normal;
font-size:24rpx;
color:#239FDF !important;
}
:deep(.uni-input-placeholder){
background:none;
font-weight:normal;
color:#BFBFBF;
font-size:28rpx;
}
:deep(.uni-forms-item__error){
padding-left:60rpx;
}
:deep(.uni-forms-item .is-required){
display: none;
}
/* 使用深度选择器 */
:deep(.uni-easyinput__content .uni-icons.uniui-clear) {
color: #BFBFBF !important;
font-weight: normal !important;
font-size:40rpx !important;
}
:deep(.uni-easyinput__content .uni-icons.uniui-clear::before){
content: "\e66c" !important;
}
.agreen-con{
margin-top:40rpx;
color:#919191;
font-size:26rpx;
display: flex;
align-items: center;
justify-content: center;
}
.agreen-con .agreen-xy{
margin-left:10rpx;
}
.agreen-con text{
color:#05A3F4;
}
.btn-plain{
color: #05A3F4 !important;
border: 1px solid #05A3F4 !important;
/* padding:0 30rpx; */
font-size:24rpx;
/* line-height: 2.1; */
/* width:230rpx; */
}
.btn-submit{
width:490rpx;
height:88rpx;
line-height: 88rpx;
background-color:#05A3F4 !important;
margin:80rpx auto 0;
font-size:36rpx;
border-radius: 44rpx;
}
.btn-submit::after{
border:none;
}
.login-bottom{
position: absolute;
background: url('../../static/images/login-txt.png');
background-size:654rpx 121rpx;
width:654rpx;
height: 121rpx;
bottom:30rpx;
left:50%;
margin-left:-327rpx;
}
</style>

234
src/pages/notice/notice.vue Normal file
View File

@@ -0,0 +1,234 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" :title="'消息'" :leftFlag="false" :rightFlag="true">
<template #right>
<view class="head-right" @click="handleReady">
<img :src="'static/images/notice/icon-clean@2x.png'" />清除未读
</view>
</template>
</customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 搜索 -->
<view class="search">
<uni-search-bar class="custom-search" radius="28" placeholder="请输入您想查询的内容或服务" clearButton="auto"
cancelButton="none" bgColor="#6FA2F8" textColor="#ffffff" @confirm="handleSearch" />
</view>
<!-- 消息列表 -->
<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"
:up="upOption" :down="downOption" :fixed="false" class="scroll-h" :class="{'loading-scroll':cssFlag}">
<view class="white-bg" v-if="list.length">
<view class="notice-list" v-for="(item, index) in list" :key="index">
<img :src="item.imgSrc" />
<view class="notice-item">
<view :class="{ 'notice-title': true, bold: item.isReady }">{{ item.title }}</view>
<view class="notice-date">{{ formatDateStr(item.date) }}</view>
<view class="dot" v-if="item.isReady"></view>
</view>
</view>
</view>
</mescroll-uni>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import customHeader from '@/components/customHeader.vue'
import { getNavBarPaddingTop } from '@/utils/system.js'
import { noticeList } from '@/api/notice.js'
import { formatTimestamp } from '@/utils/datetime.js'
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
// 获取导航栏高度用于内容区域padding
const navBarPaddingTop = ref(0);
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop() * 2;
})
// 查询搜索跳转
let handleSearch = () => {
}
const formatDateStr = (times) => {
return formatTimestamp(times)
}
// 清除未读
const handleReady = () => {
list.value.forEach(item => {
item.isReady = false;
})
}
// 查询通知列表
let list = ref([]);
const mescrollRef = ref(null);
const upOption = ref({
page: { num: 0, size: 10 },
noMoreSize: 5,
empty: { tip: '~ 空空如也 ~' },
textLoading: '加载中...',
textNoMore: '已经到底了'
});
const downOption = ref({
auto: true,
textInOffset: '下拉刷新',
textOutOffset: '释放更新',
textLoading: '刷新中...'
});
let cssFlag=ref(false);//控制样式
const mescrollInit = (mescroll) => {
cssFlag.value = true;
mescrollRef.value = mescroll;
};
// 下拉刷新
const downCallback = async (mescroll) => {
try {
setTimeout(async ()=>{
const res = await getNoticeList(1, upOption.value.page.size);
cssFlag.value = false;
list.value = res.list;
mescroll.resetUpScroll();
},500);
} catch (error) {
mescroll.endErr();
} finally {
setTimeout(async ()=>{
mescroll.endSuccess();
},500);
}
}
// 上拉加载更多
const upCallback = async (mescroll) => {
try {
setTimeout(async ()=>{
const res = await getNoticeList(mescroll.num, mescroll.size);
if (mescroll.num === 1) {
list.value = res.list;
} else {
list.value.push(...res.list);
}
mescroll.endBySize(res.list.length, res.total);
},500);
} catch (error) {
mescroll.endErr();
}
}
// 获取数据列表
const getNoticeList = (pageIndex, pageSize) => {
return new Promise(async (resolve) => {
let param = {
pageIndex,
pageSize
}
let res = await noticeList(param);
resolve({
list: res.list,
total: res.totalCount
});
});
}
</script>
<style scoped>
.all-body{
position: absolute;
/* #ifdef APP-PLUS */
top:150rpx;
height: calc(100vh - 75px);
/* #endif */
/* #ifndef APP-PLUS */
top:120rpx;
height: calc(100vh - 64px);
/* #endif */
overflow: hidden;
}
:deep(.mescroll-downwarp .downwarp-progress){
border-color:#fff !important;
}
:deep(.mescroll-downwarp .downwarp-tip){
color:#fff;
}
.white-bg {
width: 750rpx;
padding: 40rpx 0 ;
margin-bottom:0;
border-radius: 8px 8px 0 0;
}
.scroll-h{
/* #ifdef APP-PLUS */
height: calc(100vh - 120px);
/* #endif */
/* #ifndef APP-PLUS */
height: calc(100vh - 140px);
/* #endif */
}
.white-bg .notice-list {
display: flex;
padding: 0 30rpx 0 40rpx;
margin-bottom: 30rpx;
}
.white-bg .notice-list img {
width: 80rpx;
height: 80rpx;
margin-right: 30rpx;
}
.white-bg .notice-list .notice-item {
border-bottom: 1px solid #E7E7E7;
width: 570rpx;
padding-bottom: 30rpx;
position: relative;
}
.white-bg .notice-list:last-child .notice-item {
border-bottom: none;
padding-bottom: 0;
}
.white-bg .notice-list .notice-title {
font-size: 32rpx;
width: 530rpx;
white-space: nowrap;
/* 禁止换行 */
overflow: hidden;
/* 隐藏溢出内容 */
text-overflow: ellipsis;
/* 显示省略号 */
}
.white-bg .notice-list .notice-title.bold {
font-weight: bold;
}
.white-bg .notice-list .notice-date {
color: #BFBFBF;
font-size: 28rpx;
margin-top: 10rpx;
}
.white-bg .notice-list .notice-item .dot {
position: absolute;
right: 0;
top: 14rpx;
}
</style>

View File

@@ -0,0 +1,329 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 下拉刷新 -->
<mescroll-uni ref="mescrollRef" @init="mescrollInit"
:down="downOption" @down="downCallback"
:fixed="false" class="scroll-h" :style="{ paddingTop: navBarPaddingTop + 'px' }"
>
<!-- #ifdef H5 -->
<view style="height:50rpx"></view>
<!-- #endif -->
<!-- 头像 -->
<view class="head-pic">
<img class="pic-img" :src="'static/images/userinfo/icon-userinfo.png'" />
<view class="head-name">
<view class="nick">
<view class="nick-text">{{ userObj.name }}</view>
<img :src="`static/images/userinfo/num-${userObj.level}@2x.png`" class="level" />
</view>
<view class="dept">{{ userObj.deptName }} {{ userObj.jobName }}</view>
</view>
<view class="head-right">
<view class="font-ruzhi">已入职{{userObj.joinDay}}</view>
<img :src="'static/images/userinfo/icon-heart@2x.png'" />
</view>
</view>
<!-- 日常 -->
<view class="backlog-bg">
<view class="backlog-b-item">
<img :src="'static/images/business/icon-rwjh.png'" />
<view class="font-title">任务计划</view>
</view>
<view class="backlog-b-item">
<img :src="'static/images/business/icon-rb.png'" />
<view class="font-title">日报</view>
</view>
<view class="backlog-b-item">
<img :src="'static/images/business/icon-sbgl.png'" />
<view class="font-title">设备管理</view>
</view>
<view class="backlog-b-item">
<img :src="'static/images/business/icon-jxgl.png'" />
<view class="font-title">绩效管理</view>
</view>
</view>
<!-- 头像块 -->
<view class="white-bg">
<view class="list-item">
<img :src="'static/images/userinfo/icon-tx@2x.png'" class="l-icon" />
<text>头像</text>
<view class="list-right">
<img class="pic-img" :src="'static/images/userinfo/icon-userinfo.png'" />
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
</view>
</view>
</view>
<!-- 登录管理修改密码版本更新块 -->
<view class="white-bg">
<view class="list-item item-padding">
<img :src="'static/images/userinfo/icon-dlgl@2x.png'" class="l-icon" />
<view>登录管理</view>
<view class="list-right">
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
</view>
</view>
<view class="item-border"></view>
<view class="list-item item-padding">
<img :src="'static/images/userinfo/icon-xgmm@2x.png'" class="l-icon" />
<view>修改密码</view>
<view class="list-right">
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
</view>
</view>
<view class="item-border"></view>
<view class="list-item item-padding">
<img :src="'static/images/userinfo/icon-bbgx@2x.png'" class="l-icon" />
<view class="item-text">版本更新 <view class="dot"></view></view>
<view class="list-right">
<text class="item-gray">Version 1.0.0</text>
<uni-icons type="right" size="20" color="#A0A0A0"></uni-icons>
</view>
</view>
</view>
<!-- 退出登录 -->
<button type="primary" plain="true" size="small" class="logout-btn" @click="handleLoginOut">退出登录</button>
<!-- 底部加高度来避免tabbar遮挡 -->
<!-- <view class="bottom-height"></view> -->
</mescroll-uni>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// import customHeader from '@/components/customHeader.vue'
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
import { getNavBarPaddingTop } from '@/utils/system.js'
import { getUserInfo } from '@/api/auth.js'
import { useUserStore } from '@/stores/user';
const userStore = useUserStore()
// 1.头部导航栏
const navBarPaddingTop = ref(0);
onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop() * 2;
})
// 下拉刷新
const mescrollRef = ref(null);
const mescrollInit = (mescroll) => {
mescrollRef.value = mescroll;
};
const downOption = ref({
auto: true,
textInOffset: '下拉刷新',
textOutOffset: '释放更新',
textLoading: '刷新中...'
});
// 下拉刷新
const downCallback = async (mescroll) => {
try {
setTimeout(async ()=>{
// mescroll.resetUpScroll();
},500);
} catch (error) {
mescroll.endErr();
} finally {
setTimeout(async ()=>{
mescroll.endSuccess();
},500);
}
}
// 2.获取用户基本信息
let userObj = ref({});
const selectUserInfo = async ()=>{
let data = await getUserInfo({});
userObj.value = data;
}
selectUserInfo()
// 3.退出登录
const handleLoginOut = async ()=>{
userStore.logout();
uni.reLaunch({
url: '/pages/login/login',
});
}
</script>
<style scoped>
.scroll-h{
/* #ifdef APP-PLUS */
height:calc(100vh - 30px) !important;
/* #endif */
/* #ifndef APP-PLUS */
height: calc(100vh - 30px) !important;
/* #endif */
}
:deep(.mescroll-upwarp){
display:none
}
.head-pic {
display: flex;
padding: 40rpx 0rpx 10rpx 30rpx;
}
.head-pic .pic-img {
width: 110rpx;
height: 110rpx;
}
.head-pic .head-name {
color: #fff;
margin-left: 20rpx;
padding: 10rpx 0;
}
.head-pic .head-name .nick {
font-size: 36rpx;
font-weight: bold;
position: relative;
display: inline-block;
}
.head-pic .head-name .nick .nick-text {
padding-right: 35rpx;
}
.head-pic .head-name .nick .level {
position: absolute;
right: 0;
top: 30%;
transform: translateY(-50%);
width: 30rpx;
height: 32rpx;
}
.head-pic .head-name .dept {
color: #94F6FF;
font-size: 28rpx;
margin-top:10rpx;
}
.head-pic .head-right {
margin-left: auto;
background-color: #034FCC;
border-radius: 15px 0 0 15px;
color: #FFFFFF;
font-size: 24rpx;
padding: 5rpx 10rpx 5rpx 30rpx;
display: flex;
height: 36rpx;
}
.head-pic .head-right .font-ruzhi {
margin-right: 10rpx;
}
.head-pic .head-right img {
display: block;
width: 28rpx;
height: 25rpx;
margin-left: auto;
margin-top: 2px;
}
.backlog-bg {
background: url('@/static/images/userinfo/bg-me@2x.png') no-repeat;
background-size: 730rpx 250rpx;
width: 690rpx;
height: 250rpx;
margin: 0 auto;
display: flex;
padding: 0 20rpx;
align-items: center;
}
.backlog-b-item {
width: 25%;
}
.backlog-b-item img{
width: 90rpx;
height: 90rpx;
margin:0 auto 10rpx;
display: block;
}
.backlog-b-item .font-title {
font-size: 28rpx;
text-align: center;
}
.white-bg{
width:590rpx;/*690*/
padding:30rpx 50rpx;
}
.list-item{
display: flex;
align-items: center;
font-size: 32rpx;
}
.list-item.item-padding{
padding:45rpx 0;
}
.list-item.item-padding:first-child{
padding-top:20rpx;
}
.list-item.item-padding:last-child{
padding-bottom:20rpx;
}
.item-border{
height:1px;
background-color: #E7E7E7;
width:640rpx;
}
.list-item .l-icon{
width:46rpx;
/* height: 46rpx; */
margin-right:20rpx;
}
.list-item .list-right{
margin-left: auto;
display: flex;
align-items: center;
}
.list-item .list-right .pic-img{
width:72rpx;
height: 72rpx;
margin-right:20rpx;
}
.list-item .list-right .item-gray{
color:#BFBFBF;
font-size:28rpx;
margin-right: 20rpx;
}
.list-item .item-text{
display: flex;
align-items: center;
}
.list-item .dot{
margin-left:15rpx;
}
.logout-btn{
width:360rpx;
height: 80rpx;
line-height: 75rpx;
border-radius: 40rpx;
background-color: #fff !important;
font-size:36rpx !important;
margin:109rpx auto;
}
</style>

586
src/static/common.css Normal file
View File

@@ -0,0 +1,586 @@
@import '@/static/font/iconfont.css';
page {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background-color: #F5F5F5;
color: #333;
}
.container {
background-color: #F5F5F5;
margin: 0 auto;
padding: 0;
}
.con-body {
width: 100%;
margin: 0;
padding: 0;
}
.con-bg {
position: absolute;
top: 0;
right: 0;
bottom: 0;
/* #ifdef APP-PLUS */
background: url('@/static/images/bg-Blue.png') no-repeat;
background-size: 100vw 761rpx;
width: 100vw;
height: 761rpx;
left: 0;
/* #endif */
/* #ifndef APP-PLUS */
background: url('@/static/images/bg-Blue.png') no-repeat;
background-size: 750rpx 761rpx;
width: 750rpx;
height: 761rpx;
left: 50%;
margin-left: -375rpx;
/* #endif */
}
.top-height {
/* #ifdef APP-PLUS */
height: 160rpx;
/* #endif */
/* #ifndef APP-PLUS */
height: 116rpx;
/* #endif */
}
.bottom-height {
height: 60px;
}
.margin-bottom20 {
margin-bottom: 20rpx;
}
.flex {
display: flex;
}
.flex-auto {
flex: 0 0 auto;
}
.font-bold {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.font-gray {
color: #919191;
font-size: 28rpx;
}
.title {
color: #fff;
}
/* 弹出提示框修改样式 begin */
.uni-modal {
border-radius: 24rpx !important;
width: 70% !important;
}
.uni-modal .uni-modal__bd {
color: #393939 !important;
font-size: 30rpx !important;
padding-top: 50rpx !important;
word-wrap: break-word;
white-space: normal;
word-break: break-all;
}
.uni-modal .uni-modal__btn::after {
border: none !important;
}
.uni-modal .uni-modal__ft {
padding: 20rpx 40rpx 60rpx !important;
align-items: center !important;
justify-content: space-between !important;
gap: 5% !important;
/* 元素间距 */
}
.uni-modal .uni-modal__ft::after {
border: none !important;
}
.uni-modal .uni-modal__btn {
/* flex-basis: 200rpx !important; */
border-radius: 48rpx !important;
font-size: 30rpx !important;
/* width:150rpx !important; */
height: 65rpx !important;
line-height: 60rpx !important;
}
.uni-modal .uni-modal__btn_default {
border: 1px solid #05A3F4 !important;
color: #05A3F4 !important;
}
.uni-modal .uni-modal__btn_primary {
/* margin-left:20rpx !important; */
background-color: #05A3F4 !important;
color: #fff !important;
}
/* 弹出提示框修改样式 end */
/* 搜索框修改样式 begin */
.custom-search.uni-searchbar {
padding: 10rpx 30rpx 30rpx !important;
}
.custom-search .uni-searchbar__box {
height: 56rpx !important;
padding: 0 !important;
}
.custom-search .uni-searchbar__text-placeholder {
color: #fff !important;
font-size: 27rpx;
}
.custom-search .uni-searchbar__box-icon-search {
padding: 0 0 0 8px !important;
}
.custom-search .uni-searchbar__box-icon-search .uniui-search {
color: #fff !important;
font-size: 40rpx !important;
}
.custom-search .uni-input-placeholder {
color: #fff !important;
font-size: 27rpx !important;
}
.custom-search .uni-searchbar__box-icon-clear {
display: none !important;
}
/* .custom-search .uni-searchbar__box-icon-clear .uniui-clear{
color:#fff !important;
font-size: 40rpx !important;
} */
/* 搜索框修改样式 end */
.head-right {
font-size: 28rpx;
display: flex;
align-items: center;
/* #ifdef APP-PLUS */
height:160rpx;
/* #endif */
/* #ifndef APP-PLUS */
height:116rpx;
/* #endif */
}
.head-right img {
width: 28rpx;
height: 33rpx;
margin-right: 10rpx;
}
.head-right .uni-icons {
margin-right: 6rpx;
}
.border-bottom {
height: 1px;
background-color: #E7E7E7;
width: 660rpx;
}
.border-bottom.b-width {
width: 720rpx;
}
.all-body {
width: 100%;
position: absolute;
/* #ifdef APP-PLUS */
top: 88rpx;
height: calc(100vh - 44px);
/* #endif */
/* #ifndef APP-PLUS */
top: 88rpx;
height: calc(100vh);
/* #endif */
overflow: hidden;
/* z-index:999; */
}
.white-bg {
width: 630rpx;
/*690*/
margin: 10rpx auto 30rpx;
background-color: #fff;
border-radius: 10rpx;
padding: 30rpx;
border-radius: 8px;
}
.white-bg .w-b-title {
display: flex;
color: #333;
font-size: 15px;
font-weight: bold;
align-items: center;
}
.white-bg .w-b-title text {
color: #0395E0;
font-size: 13px;
font-weight: normal;
margin-left: auto;
}
.white-bg .w-b-title text .iconfont {
font-size: 11px !important;
margin-left: 10rpx;
}
.white-bg .w-b-title .btn-edit{
margin-left:auto;
background-color: #05A3F4;
padding:10rpx 30rpx;
color:#fff;
border-radius: 10rpx;
font-size:28rpx;
font-weight: normal;
cursor: pointer;
}
.white-bg .logo-list {
margin-top: 30rpx;
display: flex;
flex-wrap: wrap;
/* 关键属性:允许换行 */
gap: 20rpx;
/* 项目间距 */
}
.white-bg .logo-list .l-l-item {
width: 140rpx;
text-align: center;
margin-bottom: 30rpx;
position: relative;
}
.white-bg .logo-list .l-l-item img {
width: 90rpx;
height: 90rpx;
}
.white-bg .logo-list .l-l-item .font-gray {
color: #333;
font-size: 13px;
display: block;
}
.white-bg .logo-list .l-l-item .uni-badge--x {
position: absolute;
right: 3px;
top: -3px
}
.mar-top {
margin-top: 30rpx;
}
.white-bg .w-b-title .yellow-bg {
background-color: #FFF5DD;
border-radius: 5px;
padding: 10rpx 20rpx;
display: flex;
align-items: center;
margin-left: auto;
font-weight: normal;
}
.white-bg .w-b-title .yellow-bg .iconfont {
font-size: 35rpx;
color: #6CBCFF;
margin-right: 12rpx;
}
.white-bg .w-b-title .yellow-bg .text-black {
color: #333;
font-size: 12px;
}
.white-bg .w-b-title .yellow-bg .picker-icon {
color: #282728;
font-size: 24rpx;
}
.white-bg .w-b-title .yellow-bg .uni-input {
font-size: 24rpx;
}
.news-list {
margin: 0;
}
.news-list .news-item {
display: flex;
border-bottom: 1px solid #E7E7E7;
padding: 30rpx 0 40rpx;
}
.news-list .news-item:last-child {
border-bottom: none;
}
.news-list .news-item .n-i-title {
font-size: 32rpx;
}
.news-list .news-item .n-i-title .n-i-date {
color: #BFBFBF;
font-size: 28rpx;
margin-top: 10rpx;
}
.news-list .news-item img {
width: 240rpx;
height: 160rpx;
margin-left: auto;
}
uni-button[size='mini'] {
font-size: 28rpx !important;
}
uni-button[type='primary'][plain] {
color: #05A3F4 !important;
border: 1px solid #05A3F4 !important;
}
.dot {
width: 16rpx;
height: 16rpx;
background-color: #FF2B44;
border-radius: 50%;
}
/* CRM 样式 begin */
.loading-scroll .mescroll-upwarp .upwarp-progress{
border-color: #fff !important;
}
.loading-scroll .mescroll-upwarp .upwarp-tip{
color: #fff !important;
}
.mescroll-downwarp .downwarp-progress,
.mescroll-downwarp .mescroll-rotate {
border-color: #fff !important;
}
.mescroll-downwarp .downwarp-tip,
.mescroll-downwarp .upwarp-tip {
color: #fff !important;
}
.report-list {}
.report-list .title {
color: #3384DF;
font-size: 38rpx;
font-weight: bold;
}
.report-list .r-list {
padding: 20rpx 0;
display: flex;
align-items: center;
justify-content: space-between;
}
.report-list .r-list .r-left {
font-size: 28rpx;
}
.report-list .r-list .r-name {
color: #333;
font-weight: bold;
font-size: 28rpx;
}
.report-list .r-list .r-gray {
color: #919191;
font-size: 28rpx;
}
.report-list .r-list .r-right {
margin-left: auto;
text-align: right;
color: #919191;
font-size: 28rpx;
}
.report-list .r-list .btn-orange,
.report-list .r-list .btn-pink,
.report-list .r-list .btn-gray {
background-color: #FF9638;
color: #fff;
border: none;
padding: 10rpx 15rpx;
border-radius: 10rpx;
font-weight: normal;
}
.report-list .r-list .btn-orange::after,
.report-list .r-list .btn-pink::after,
.report-list .r-list .btn-gray::after {
display: none;
}
.report-list .r-list .btn-pink {
background-color: #FF687A;
padding: 10rpx 40rpx;
}
.report-list .r-list .btn-gray {
background-color: #F0F0F0;
padding: 10rpx 20rpx;
color: #919191;
font-weight: normal;
}
.report-list .r-list .btn-blue {
background-color: #05A3F4;
padding: 10rpx 30rpx;
color: #fff;
font-weight: normal;
}
.form-con {
padding: 0 30rpx;
}
.form-con .uni-forms-item__label {
font-weight: bold;
font-size: 32rpx;
}
.form-con .f-c-right .uni-forms-item__content {
text-align: right;
color: #919191;
}
.form-con .f-c-right .f-c-text {
color: #919191;
display: flex;
align-items: center;
justify-content: flex-end;
padding-top: 20rpx;
}
.form-con .uni-forms-item {
margin-bottom: 25rpx !important;
}
.form-con .txt-right {
padding-right: 40rpx;
}
.form-con .footer-con {
display: flex;
}
/* .form-con .form-texarea .is-textarea{
min-height: 230rpx;
} */
.form-con .form-picker {
border: 1px solid #E7E7E7;
border-radius: 10rpx;
padding: 20rpx;
color: #919191;
}
.form-con .form-picker .uniui-down {
margin-left: auto;
}
.form-con .footer-con .btn-default,
.form-con .footer-con .btn-primary {
background-color: #fff;
border: 1px solid #05A3F4;
color: #05A3F4;
border-radius: 25px;
padding: 0rpx 80rpx;
font-size: 34rpx;
margin-left: 0;
margin-right: 20rpx;
}
.form-con .footer-con .btn-primary {
background-color: #05A3F4;
border: 1px solid #05A3F4;
color: #fff;
padding: 0rpx 60rpx;
}
.form-con .footer-con .btn-default::after,
.form-con .footer-con .btn-primary::after {
display: none;
}
.my-table {
width: 100%;
border-collapse: collapse;
}
.my-table th,
.my-table td {
border: 1px solid #E9E9E9;
padding: 12rpx 10rpx;
text-align: center;
font-size:26rpx;
}
.my-table th {
background-color: #ffffff;
font-weight: bold;
}
.my-table tr:nth-child(even) {
background-color: #F7F7F7;
}
.my-table tr:hover {
background-color: #f0f0f0;
}
.my-table .tab-width100{
width:100rpx;
word-wrap: break-word;
overflow-wrap: break-word;
}
.my-table .tab-width160{
width:160rpx;
word-wrap: break-word;
overflow-wrap: break-word;
}
.my-table .txtLeft{
text-align: left;
}
/* CRM 样式 end */

View File

@@ -0,0 +1,81 @@
@font-face {
font-family: "iconfont"; /* Project id 4386638 */
src: url('~@/static/font/iconfont.ttf?t=1703474652463') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-phonebaocun:before {
content: "\e80b";
}
.icon-phoneshizhong:before {
content: "\e74f";
}
.icon-phonesaoyisao:before {
content: "\e891";
}
.icon-qh-right:before {
content: "\e603";
}
.icon-qh-right2:before {
content: "\e602";
}
.icon-share:before {
content: "\ebab";
}
.icon-link:before {
content: "\e63a";
}
.icon-up:before {
content: "\e601";
}
.icon-down:before {
content: "\e671";
}
.icon-bonus:before {
content: "\e646";
}
.icon-message:before {
content: "\e645";
}
.icon-lightning:before {
content: "\e6b9";
}
.icon-watch1:before {
content: "\e66d";
}
.icon-watch:before {
content: "\e614";
}
.icon-code:before {
content: "\e61e";
}
.icon-phone:before {
content: "\e678";
}
.icon-dang:before {
content: "\e600";
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
src/static/images/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Some files were not shown because too many files have changed in this diff Show More