提交 9a64445d authored 作者: lidongxu's avatar lidongxu

feat(mobile/audit_activity): 新增:勤策内置移动端_售点稽核部分页面样式搭建

上级 9c71634b
......@@ -5,7 +5,7 @@ VITE_APP_ENV = 'development'
# 公共资源前缀地址
VITE_APP_PUBLIC_PATH = '/'
# 基地址
# 链路中心基地址
VITE_APP_BASE_API = '/dev-api' # 小卤
# VITE_APP_BASE_API = '/ql_local' # 秋林本地
# VITE_APP_BASE_API = '/bc_local' # 本才本地
......@@ -13,6 +13,7 @@ VITE_APP_PROMOTION = '/dev-promotion-api' # 促销
# 飞书服务回调地址(本地测试已经通过并上线,后台飞书登录接口重定向地址已经不是 localhost 了所以本地开发无需使用飞书登录,线上已经可用)
VITE_APP_REDIRECT_URL = 'http://localhost:8085'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa-qa.wxl66.cn'
# 积木报表预览地址
......@@ -22,5 +23,8 @@ VITE_APP_REPORT_SHARE_PREVIEW_URL = '#/report'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = '#/report/jmreport/index'
# 模板表格
# 促销计划上报模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF4.0.xlsx'
# 腾讯地图接口地址
VITE_APP_TENCENT_MAP_API = '/tencent_map_api'
\ No newline at end of file
......@@ -5,13 +5,14 @@ VITE_APP_ENV = 'production'
# 公共资源前缀地址
VITE_APP_PUBLIC_PATH = './'
# 基地址
# 链路中心基地址
VITE_APP_BASE_API = '/api' # 小卤
# VITE_APP_PROMOTION = 'https://promotion.wxl66.cn' # 促销
VITE_APP_PROMOTION = '/promotion-api' # 促销
# 飞书服务回调地址
VITE_APP_REDIRECT_URL = 'https://sfa.wxl66.cn/link/'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa.wxl66.cn'
# 积木报表预览地址
......@@ -21,5 +22,8 @@ VITE_APP_REPORT_SHARE_PREVIEW_URL = '#/report'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = '#/report/jmreport/index'
# 模板表格
# 促销计划上报模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF4.0.xlsx'
# 腾讯地图接口地址
VITE_APP_TENCENT_MAP_API = 'https://apis.map.qq.com/ws/geocoder/v1'
\ No newline at end of file
......@@ -5,12 +5,13 @@ VITE_APP_ENV = 'staging'
# 公共资源前缀地址
VITE_APP_PUBLIC_PATH = './'
# 基地址
# 链路中心基地址
VITE_APP_BASE_API = '/api' # 小卤
VITE_APP_PROMOTION = '/promotion-api' # 促销
# 飞书服务回调地址
VITE_APP_REDIRECT_URL = 'https://sfa-qa.wxl66.cn/'
# 积木报表服务地址
VITE_APP_REPORT_URL = 'https://sfa-qa.wxl66.cn'
# 积木报表预览地址
......@@ -20,5 +21,8 @@ VITE_APP_REPORT_SHARE_PREVIEW_URL = '#/report'
# 积木报表编辑地址
VITE_APP_REPORT_EDIT_URL = '#/report/jmreport/index'
# 模板表格
# 促销计划上报模板表格
VITE_APP_PLAN_TEMPLATE_EXCEL = 'https://link-promotion.oss-cn-shanghai.aliyuncs.com/file/%E6%96%B0%E5%A2%9E%E6%88%96%E4%BF%AE%E6%94%B9%E8%AE%A1%E5%88%92-%E6%A8%A1%E6%9D%BF4.0.xlsx'
# 腾讯地图接口地址
VITE_APP_TENCENT_MAP_API = 'https://apis.map.qq.com/ws/geocoder/v1'
\ No newline at end of file
......@@ -208,7 +208,7 @@
<script src="https://lf-scm-cn.feishucdn.com/lark/op/h5-js-sdk-1.5.34.js"
type="text/javascript"></script>
<!-- 腾讯地图 SDK -->
<script src="https://map.qq.com/api/gljs?v=1.exp&key=EOGBZ-US2RW-YF5RC-OKB5O-R6BUK-2LFVS"></script>
<script src="https://map.qq.com/api/gljs?v=1.exp&key=UTEBZ-UJ3KG-OORQO-QT3PT-JDWU7-YRBZA"></script>
<% if
(systemConfig.env
!=='development'
......
import request from '@/utils/request'
// 逆地址编码
export const getLocation = (params) => {
return request({
baseURL: import.meta.env.VITE_APP_TENCENT_MAP_API,
url: '/',
params: {
location: params.lat + ',' + params.lng, // GCJ02坐标系(经度,维度)
key: 'UTEBZ-UJ3KG-OORQO-QT3PT-JDWU7-YRBZA'
}
})
}
\ No newline at end of file
export * from './common/login'
export * from './common/menu'
export * from './common/openQuery'
export * from './common/region'
export * from './common/upload'
export * from './bi/competitor'
export * from './bi/finance'
export * from './bi/livecate'
......@@ -11,6 +6,12 @@ export * from './bi/report'
export * from './bi/sale'
export * from './bi/store'
export * from './bi/supply'
export * from './common/location'
export * from './common/login'
export * from './common/menu'
export * from './common/openQuery'
export * from './common/region'
export * from './common/upload'
export * from './jimu/design'
export * from './jimu/ext'
export * from './jimu/list'
......
export const typeOptions = [ // 门店类型
{
text: 'KA',
value: 'KA'
}, {
text: 'BC',
value: 'BC'
},
{
text: 'CVS',
value: 'CVS'
},
{
text: '零食',
value: '零食'
},
{
text: '批发',
value: '批发'
}
]
\ No newline at end of file
......@@ -12,6 +12,7 @@ import '@/assets/styles/vant.scss'
// 只有在移动端引入
if (isMobile()) {
(function () {
// flexible.js
const docEl = document.documentElement;
const designWidth = 375; // 设计稿宽度(375px设计稿)
const baseSize = 37.5; // 与 postcss-pxtorem 的 rootValue 一致
......
<template>
<!-- 稽查任务 -->
<div class="wrap">
<van-nav-bar left-arrow
left-text="返回"
@click-left="router.back()">
</van-nav-bar>
<div class="content">
<p class="title">稽查任务:</p>
<!-- 终端名称 -->
<div class="terminal-wrap">
<div>
<van-image width="2.25rem"
height="3.5rem"></van-image>
</div>
<div class="terminal-info">
<p>终端名称:世纪港湾广缘超时</p>
<p>终端编码:P0500052306</p>
<p>经销商:秦皇岛红朗森商贸有限公司</p>
<p>地址:河北省秦皇岛市海港区北环路街道秦皇岛冻结附录</p>
</div>
</div>
<!-- 地址 -->
<div class="location-wrap">
<van-icon name="location-o"
class="location-icon" />
<van-loading size="20px"
v-if="addressLoading">正在获取您的位置...</van-loading>
<p v-else>{{ addressStr }}</p>
<van-icon name="replay"
class="refresh-icon"
@click="handleClickLocation" />
</div>
<!-- 门头照上传 -->
<div class="header-photo-section">
<van-field label="门头照"
label-align="top">
<template #input>
<van-uploader :max-count="2"
accept="image/*"
v-model="form.posPhotos"
:after-read="posPhotosRead"
preview-size="120"
@delete="deletePosPhotos">
</van-uploader>
</template>
</van-field>
</div>
<!-- 门店类型选择 -->
<van-field :model-value="form.selectedType.join('')"
label="门店类型"
label-align="top"
placeholder="请选择"
@click="showTypePopup = true" />
<van-popup v-model:show="showTypePopup"
position="bottom"
@close="showTypePopup = false">
<van-picker v-model="form.selectedType"
:columns="typeOptions"
@confirm="handleTypeConfirm"
@cancel="showTypePopup = false" />
</van-popup>
<!-- 信息填写组 -->
<van-tabs v-model:active="active" class="tabs">
<van-tab title="常规陈列">
<convention />
</van-tab>
<van-tab title="档期陈列">
<scheduleDisplay />
</van-tab>
<van-tab title="档期补差">
<scheduleAdjustment />
</van-tab>
<van-tab title="品类信息">
<categoryInfomation />
</van-tab>
</van-tabs>
</div>
</div>
</template>
<script setup>
import { uploadFileToOSSAPI, getLocation } from '@/api'
import { typeOptions as typeOption } from '@/views/mobile/constant'
import convention from './tabs/categoryInfomation.vue'
import scheduleDisplay from './tabs/scheduleDisplay.vue'
import scheduleAdjustment from './tabs/scheduleAdjustment.vue'
import categoryInfomation from './tabs/categoryInfomation.vue'
const router = useRouter();
const form = reactive({
selectedType: [], // 门店类型
})
// 地理定位
const addressStr = ref('')
const addressLoading = ref(true)
const getLocationFn = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
async function (position) {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
const res = await getLocation({
lat,
lng
})
const { standard_address } = res.result.formatted_addresses
addressStr.value = standard_address
addressLoading.value = false
},
function (error) {
showDialog({
title: '系统提示',
message: '请开启软件的定位授权,再试' + JSON.stringify(error),
})
},
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
} else {
showDialog({
title: '系统提示',
message: '你的设备不支持定位',
})
}
}
getLocationFn()
const handleClickLocation = () => {
addressLoading.value = true
setTimeout(() => {
getLocationFn()
}, 1500)
}
// 门头照
// 门头照上传逻辑
// POS 两张照片
const posPhotosRead = async (file) => {
// 处理上传的文件
const date = new Date()
const month = date.getMonth() + 1
const theDate = date.getDate()
const pictureUrl = await uploadFileToOSSAPI(`risk/${date.getFullYear()}-${month}/${theDate}/${planId.value}/${employeeNo}/${uuidv4()}.png`, file.file)
// 判断 objectUrl
const index = form.value.posPhotos.findIndex(o => o.objectUrl)
form.value.posPhotos[index] = {
url: pictureUrl
}
}
// 删除 照片
const deletePosPhotos = async () => {
if (isInitializing.value) return
showNotify({ type: 'success', message: 'POS 照片删除成功' })
}
// 门店类型
const showTypePopup = ref(false)
const typeOptions = ref(typeOption); // 门店类型
// 确认门店类型
const handleTypeConfirm = (val) => {
form.selectedType = val;
showTypePopup.value = false;
};
// 信息填写组
const active = ref(0)
</script>
<style scoped>
.wrap {
background-color: #f5f5f5;
min-height: 100vh;
font-size: 16px;
.content {
padding: 20px;
.title {
margin: 0;
font-size: 14px;
font-weight: bold;
}
/* 终端名称信息 */
.terminal-wrap {
margin-top: 10px;
padding: 10px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
display: flex;
gap: 20px;
.terminal-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
p {
font-size: 14px;
margin: 0;
color: #a1a1a1;
}
p:first-child {
color: black;
font-size: 15px;
font-weight: bold;
}
}
}
/* 位置信息 */
.location-wrap {
margin-top: 10px;
padding: 10px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: space-between;
.location-icon {
font-size: 20px;
color: #da5c50;
}
p {
font-size: 14px;
margin: 0;
}
}
/* 门头照 */
.header-photo-section {
margin-top: 10px;
::v-deep(.van-uploader__upload) {
width: 80px !important;
height: 80px !important;
border: 1px dashed #ccc;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f8f8f8;
}
}
/* 信息填写组 */
.tabs{
margin-top: 10px;
}
}
}
</style>
\ No newline at end of file
<template>
<div>
常规陈列
<!-- 常规陈列 -->
</div>
</template>
<script setup>
</script>
<style scoped></style>
\ No newline at end of file
<template>
<div>
档期补差
<!-- 档期补差 -->
</div>
</template>
<script setup>
</script>
<style scoped></style>
\ No newline at end of file
<template>
<div>
档期陈列
<!-- 档期陈列 -->
</div>
</template>
<script setup>
</script>
<style scoped></style>
\ No newline at end of file
<template>
<div class="mobile-page-container">
<van-nav-bar left-arrow
@click-left="router.back()">
</van-nav-bar>
<div class="terminal-info-container">
<p class="title">录入终端信息</p>
<div class="content">
<!-- 终端名称输入 -->
<van-field v-model="terminalName"
......@@ -42,9 +45,10 @@
</van-popup>
</div>
</div>
<!-- 操作按钮 -->
<div class="button-group">
<div class="content">
<van-button type="danger"
block
@click="handleCreateTask">
......@@ -57,38 +61,21 @@
</van-button>
</div>
</div>
</div>
</template>
<script setup>
import { uploadFileToOSSAPI } from '@/api'
import { v4 as uuidv4 } from 'uuid';
import { typeOptions as typeOption } from '@/views/mobile/constant'
const router = useRouter();
const form = ref({
selectedType: []
})
const terminalName = ref('');
const headerPhotoUrl = ref('');
const typeOptions = ref([
{
text: 'KA',
value: 'KA'
}, {
text: 'BC',
value: 'BC'
},
{
text: 'CVS',
value: 'CVS'
},
{
text: '零食',
value: '零食'
},
{
text: '批发',
value: '批发'
}
]); // 示例类型
const typeOptions = ref(typeOption); // 门店类型
const selectedType = ref('');
const showTypePopup = ref(false);
......@@ -149,12 +136,17 @@ const handleCancel = () => {
<style lang="scss"
scoped>
.mobile-page-container {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f8f8f8;
.terminal-info-container {
flex: 1;
padding: 20px;
box-sizing: border-box;
background-color: #f8f8f8;
min-height: 100vh;
background-color: transparent;
.title {
font-size: 14px;
......@@ -167,46 +159,30 @@ const handleCancel = () => {
background-color: white;
}
.input-field {
margin-bottom: 12px;
}
.header-photo-section {
margin-bottom: 12px;
.header-photo-label {
display: block;
cursor: pointer;
.header-photo-placeholder {
width: 80px;
height: 80px;
::v-deep(.van-uploader__upload) {
width: 80px !important;
height: 80px !important;
border: 1px dashed #ccc;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f8f8f8;
}
}
.preview-image {
width: 80px;
height: 80px;
margin-top: 8px;
border-radius: 4px;
}
}
.button-group {
margin-top: auto;
margin-bottom: 20px;
.content {
background-color: transparent;
display: flex;
flex-direction: column;
gap: 12px;
margin-top: 24px;
van-button {
flex: 1;
padding: 0 50px;
}
}
}
......
<template>
<div class="search-container">
<van-nav-bar left-arrow
@click-left="router.back()">
<template #right>
<van-tag type="primary"
round
size="medium"
@click="router.push({ path: '/taskList' })">稽查记录</van-tag>
</template>
</van-nav-bar>
<!-- 头部区域 -->
<div class="header-wrap">
<span>终端</span>
<span>终端门店列表</span>
<!-- 搜索区域 -->
<van-search v-model="searchVal"
placeholder="请输入勤策终端编码/名称"
......@@ -18,12 +27,7 @@
</van-button>
</template>
</van-search>
<van-tag type="primary"
round
size="medium">记录</van-tag>
</div>
<!-- 结果列表 -->
<van-list v-model:loading="loading"
:finished="finished"
......@@ -32,7 +36,8 @@
<van-cell-group inset>
<van-cell v-for="(item, index) in resultList"
:key="index"
class="result-item">
class="result-item"
@click="handleClickStore(item)">
<template #title>
<div class="item-title">{{ item.name }}{{ item.code }}</div>
</template>
......@@ -127,6 +132,18 @@ const handleClear = () => {
resultList.value = mockData;
showEmpty.value = false;
};
// 门店点击
const handleClickStore = (item) => {
router.push({
path: '/inspectionTask',
query: {
storeName: item.name,
storeCode: item.code
}
})
}
</script>
<style lang="scss"
......@@ -137,7 +154,7 @@ const handleClear = () => {
.search-container {
width: $base-width;
// width: $base-width;
margin: 0 auto;
font-size: $base-font-size;
box-sizing: border-box;
......
<template>
<!-- 任务列表 -->
<van-nav-bar left-arrow
@click-left="router.back()">
</van-nav-bar>
<van-list v-model:loading="loading"
:finished="finished"
finished-text="没有更多结果了"
class="result-list">
<van-cell-group inset>
<van-cell v-for="(item, index) in resultList"
:key="index"
class="result-item"
@click="handleClickStore(item)">
<template #title>
<div class="item-title">{{ item.name }}{{ item.code }}</div>
</template>
<template #label>
<div class="item-company">{{ item.company }}</div>
<div class="item-address">{{ item.address }}</div>
</template>
</van-cell>
</van-cell-group>
</van-list>
</template>
<script setup>
const router = useRouter();
const resultList = ref([
{
name: '门店1',
code: '123456',
company: '公司1',
address: '地址1'
},
{
name: '门店2',
code: '123457',
company: '公司2',
address: '地址2'
},
{
name: '门店3',
code: '123458',
company: '公司3',
address: '地址3'
},
])
</script>
<style scoped></style>
\ No newline at end of file
<style scoped>
.result-list {
background-color: transparent;
padding: 0 20px;
margin-top: 10px;
.van-cell-group {
margin: 0;
background-color: transparent;
.result-item {
background-color: white;
margin-bottom: 10px;
.item-title {
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.item-company {
font-size: 14px;
color: #a6a4a4;
margin-bottom: 2px;
}
.item-address {
font-size: 13px;
color: #a6a4a4;
line-height: 1.4;
}
}
}
}
</style>
\ No newline at end of file
......@@ -41,10 +41,21 @@ export const constantMobileRoutes = [
name: 'logistics'
},
{
path: 'newTerminal',
path: 'newTerminal', // 新建终端门店
component: () => import('@/views/mobile/pages/audit_activity/sales_point_inspection/examine/newTerminal'),
name: 'newTerminal'
}
},
{
path: 'taskList', // 稽查记录
component: () => import('@/views/mobile/pages/audit_activity/sales_point_inspection/examine/taskList'),
name: 'taskList'
},
{
path: 'inspectionTask', // 稽查任务(终端门店)
component: () => import('@/views/mobile/pages/audit_activity/sales_point_inspection/examine/inspectionTask/index'),
name: 'inspectionTask'
},
]
}
]
\ No newline at end of file
......@@ -22,6 +22,11 @@ export default defineConfig(({ mode, command }) => {
host: true,
open: true,
proxy: {
'/tencent_map_api': {
target: 'https://apis.map.qq.com/ws/geocoder/v1',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/tencent_map_api/, '')
},
'/dev-api': {
target: 'http://192.168.100.55:8080',
// target: 'https://sfa-qa.wxl66.cn/api',
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论