'初始化框架'

This commit is contained in:
hejin 2025-08-10 23:06:19 +08:00
commit 4c8780569f
42 changed files with 10194 additions and 0 deletions

3
.env.development Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV='development'
VITE_APP_TITLE='开发环境'
VITE_APP_BASE_API='http://114.132.197.85:8099'

3
.env.production Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV='production'
VITE_APP_TITLE='生产环境'
VITE_APP_BASE_API=''

3
.env.test Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV='test'
VITE_APP_TITLE='测试环境'
VITE_APP_BASE_API=''

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
README.md Normal file
View File

@ -0,0 +1,24 @@
## 介绍
- - DCloud / uni-preset-vue 基于这个框架再次进行修改
- - 基于vue3、pinia、uni-ui、luch-request、sass、uniapp强大的移动端框架。
- - 开箱即用省去部分的开发时间支持微信小程序和h5的框架
- - 本框架已实现请求数据库的封装pinia的基本使用跨域请求数据、以及全局的自定义样式弹窗个人感觉比uniapp里面的好看和实用
## 安装依赖
建议node版本在18版本
pnpm install
# 开发注意
1. 处理富文本必须使用rich-text标签用html-parser.js来转换数据
2. 简单的弹窗提醒用公共组件ConfirmPopup
3. 消息提醒必须用uni.$showTost
# h5打包发布
配置相对应的env文件
测试环境 pnpm run build:h5:test
正式环境 pnpm run build:h5
# 如果使用pnpm install报错 应该是报的版本问题Error: Expected "0.17.19" but got "0.16.17"
1. pnpm store prune
2. 删除node_modules
3. 手动安装esbuild@0.17.19

20
index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

76
package.json Normal file
View File

@ -0,0 +1,76 @@
{
"name": "uni-preset-vue",
"version": "0.0.0",
"scripts": {
"dev:app": "uni -p app",
"dev:app-android": "uni -p app-android",
"dev:app-ios": "uni -p app-ios",
"dev:custom": "uni -p",
"dev:h5": "cross-env vite --mode development",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:mp-xhs": "uni -p mp-xhs",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:app": "uni build -p app",
"build:app-android": "uni build -p app-android",
"build:app-ios": "uni build -p app-ios",
"build:custom": "uni build -p",
"build:h5:test": "cross-env NODE_ENV=test vite build --mode test",
"build:h5": "cross-env NODE_ENV=production vite build --mode production",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-jd": "uni build -p mp-jd",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:mp-xhs": "uni build -p mp-xhs",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-3090420231025001",
"@dcloudio/uni-app-plus": "3.0.0-3090420231025001",
"@dcloudio/uni-components": "3.0.0-3090420231025001",
"@dcloudio/uni-h5": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-alipay": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-baidu": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-jd": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-lark": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-qq": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-toutiao": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-weixin": "3.0.0-3090420231025001",
"@dcloudio/uni-mp-xhs": "3.0.0-3090420231025001",
"@dcloudio/uni-quickapp-webview": "3.0.0-3090420231025001",
"@dcloudio/uni-ui": "^1.5.6",
"cross-env": "^7.0.3",
"esbuild": "0.17.19",
"lodash": "^4.17.21",
"pinia": "2.0.3",
"sass": "1.63.4",
"vue": "^3.2.45",
"vue-i18n": "9.1.9"
},
"devDependencies": {
"@dcloudio/types": "^3.3.2",
"@dcloudio/uni-automator": "3.0.0-3090420231025001",
"@dcloudio/uni-cli-shared": "3.0.0-3090420231025001",
"@dcloudio/uni-stacktracey": "3.0.0-3090420231025001",
"@dcloudio/vite-plugin-uni": "3.0.0-3090420231025001",
"@vue/runtime-core": "^3.2.45",
"vite": "4.0.3"
}
}

7514
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

19
src/App.vue Normal file
View File

@ -0,0 +1,19 @@
<script>
export default {
globalData: {
}
}
</script>
<script setup>
import { onLaunch } from "@dcloudio/uni-app";
import { ref } from "vue";
onLaunch( async(e) => {
})
</script>
<style lang="scss">
@import 'assets/css/common.scss';
</style>

10
src/api/index.js Normal file
View File

@ -0,0 +1,10 @@
import request from '@/utils/request';
/**
* @function
* @return
**/
export const getFriendLink = (params) => {
return request.get('',{params})
}

View File

@ -0,0 +1,12 @@
view{
box-sizing: border-box;
}
button {
margin: 0 !important;
border: none !important;
border-radius: 16rpx;
}
.pages {
height: 100vh;
font-size: 26rpx;
}

View File

@ -0,0 +1,137 @@
<template>
<view>
<uni-popup ref="popup" background-color="#fff" :is-mask-click="processLoad.isMaskClick" :mask-click="processLoad.isMaskClick">
<view class="popup-content">
<view class="header">{{params.title}}</view>
<view class="main">
<slot name="main">
<view class="content-box" v-if="!processLoad.isHtml" :style="processLoad.contentStyle">
{{params.content}}
</view>
<rich-text v-else class="content-box" :style="processLoad.contentStyle" :nodes="params.content"></rich-text>
</slot>
</view>
<slot name="footer">
<view class="footer">
<view v-if="processLoad.showCancel" class="confirm-btn1 cancel" @click="close" type="primary">取消</view>
<button :loading="confirmLoading" class="confirm-btn1" @click="confirm" type="primary">{{processLoad.confirmMsg}}</button>
</view>
</slot>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref,computed } from 'vue';
const popup = ref()
const confirmLoading = ref(false)
const params = ref({})
const processLoad = computed(() => {
const {
isHtml=false, // html
showCancel=true, //
isMaskClick=false, //
isLoading=false, // loading
confirmMsg='确定', //
close=()=>{}, //
confirm=()=>{}, //
contentStyle // content
} = params.value.load || {}
return {
isHtml,
showCancel,
isMaskClick,
isLoading,
confirmMsg,
close,
confirm,
contentStyle
}
})
const setParams = (row) => {
params.value = row
}
const close = () => {
processLoad.value.close()
popup.value.close()
}
const confirm = async () => {
if (processLoad.value.isLoading) {
confirmLoading.value = true
try {
await processLoad.value.confirm()
confirmLoading.value = false
popup.value.close()
} catch(err) {
confirmLoading.value = false
popup.value.close()
}
} else {
popup.value.close()
processLoad.value.confirm()
}
}
defineExpose({
popup,
setParams
})
</script>
<style lang="scss" scoped>
:deep(.uni-popup__wrapper) {
border-radius: 16rpx;
overflow: hidden;
}
.popup-content {
width: 668rpx;
.header {
width: 100%;
height: 90rpx;
line-height: 90rpx;
text-align: center;
color: #4679f0;
font-size: 35rpx;
background-color: #eff3ff;
}
.main {
width: 100%;
max-height: 800rpx;
margin-bottom: 20rpx;
padding: 22rpx;
overflow-y: auto;
.content-box {
margin-top: 40rpx;
text-align: center;
}
}
}
.footer {
display: flex;
align-items: center;
justify-content: space-around;
padding-bottom: 20rpx;
.confirm-btn1 {
width: 260rpx;
height: 80rpx;
background: #4679F0;
border-radius: 16rpx;
font-size: 30rpx;
color: #FFFFFF;
text-align: center;
line-height: 80rpx;
}
.cancel {
border: 1px solid #e8e8e8;
background: none !important;
color:#333333;
}
}
</style>

25
src/hooks/utils.js Normal file
View File

@ -0,0 +1,25 @@
import { ref, nextTick } from 'vue';
// 公共弹窗hooks
export const useComfirmPopup = () => {
const popupShow = ref(false)
const comfirmPopupRef = ref()
const openPopup = (row = {}) => {
popupShow.value = true
nextTick(() => {
comfirmPopupRef.value.setParams({
title: row.title || '温馨提示',
content: row.content || '',
load: row.load || {}
})
comfirmPopupRef.value.popup.open()
})
}
return {
popupShow,
comfirmPopupRef,
openPopup
}
}

View File

@ -0,0 +1,132 @@
import buildURL from '../helpers/buildURL'
import buildFullPath from '../core/buildFullPath'
import settle from '../core/settle'
import {isUndefined} from "../utils"
/**
* 返回可选值存在的配置
* @param {Array} keys - 可选值数组
* @param {Object} config2 - 配置
* @return {{}} - 存在的配置项
*/
const mergeKeys = (keys, config2) => {
let config = {}
keys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
return config
}
export default (config) => {
return new Promise((resolve, reject) => {
let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params, config.paramsSerializer)
const _config = {
url: fullPath,
header: config.header,
complete: (response) => {
config.fullPath = fullPath
response.config = config
response.rawData = response.data
try {
let jsonParseHandle = false
const forcedJSONParsingType = typeof config.forcedJSONParsing
if (forcedJSONParsingType === 'boolean') {
jsonParseHandle = config.forcedJSONParsing
} else if (forcedJSONParsingType === 'object') {
const includesMethod = config.forcedJSONParsing.include || []
jsonParseHandle = includesMethod.includes(config.method)
}
// 对可能字符串不是json 的情况容错
if (jsonParseHandle && typeof response.data === 'string') {
response.data = JSON.parse(response.data)
}
// eslint-disable-next-line no-empty
} catch (e) {
}
settle(resolve, reject, response)
}
}
let requestTask
if (config.method === 'UPLOAD') {
delete _config.header['content-type']
delete _config.header['Content-Type']
let otherConfig = {
// #ifdef MP-ALIPAY
fileType: config.fileType,
// #endif
filePath: config.filePath,
name: config.name
}
const optionalKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef H5
'file',
// #endif
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
'timeout',
// #endif
'formData'
]
requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
} else if (config.method === 'DOWNLOAD') {
const optionalKeys = [
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
'timeout',
// #endif
// #ifdef MP
'filePath',
// #endif
]
requestTask = uni.downloadFile({..._config, ...mergeKeys(optionalKeys, config)})
} else {
const optionalKeys = [
'data',
'method',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4',
// #endif
// #ifdef MP-WEIXIN
'enableHttp2',
'enableQuic',
// #endif
// #ifdef MP-TOUTIAO || MP-WEIXIN
'enableCache',
// #endif
// #ifdef MP-WEIXIN
'enableHttpDNS',
'httpDNSServiceId',
'enableChunked',
'forceCellularNetwork',
// #endif
// #ifdef MP-ALIPAY
'enableCookie',
// #endif
// #ifdef MP-BAIDU
'cloudCache',
'defer'
// #endif
]
requestTask = uni.request({..._config, ...mergeKeys(optionalKeys, config)})
}
if (config.getTask) {
config.getTask(requestTask, config)
}
})
}

View File

@ -0,0 +1,51 @@
'use strict'
function InterceptorManager() {
this.handlers = []
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
})
return this.handlers.length - 1
}
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null
}
}
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
this.handlers.forEach(h => {
if (h !== null) {
fn(h)
}
})
}
export default InterceptorManager

View File

@ -0,0 +1,201 @@
/**
* @Class Request
* @description luch-request http请求插件
* @Author lu-ch
* @Email webwork.s@qq.com
* 文档: https://www.quanzhan.co/luch-request/
* github: https://github.com/lei-mu/luch-request
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
*/
import dispatchRequest from './dispatchRequest'
import InterceptorManager from './InterceptorManager'
import mergeConfig from './mergeConfig'
import defaults from './defaults'
import { isPlainObject } from '../utils'
import clone from '../utils/clone'
export default class Request {
/**
* @param {Object} arg - 全局配置
* @param {String} arg.baseURL - 全局根路径
* @param {Object} arg.header - 全局header
* @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
* @param {String} arg.dataType = [json] - 全局默认的dataType
* @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType支付宝小程序不支持
* @param {Object} arg.custom - 全局默认的自定义参数
* @param {Number} arg.timeout - 全局默认的超时时间单位 ms默认60000H5(HBuilderX 2.9.9+)APP(HBuilderX 2.9.9+)微信小程序2.10.0支付宝小程序
* @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书默认true.仅App安卓端支持HBuilderX 2.3.3+
* @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证cookies默认false仅H5支持HBuilderX 2.6.15+
* @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4默认false App-Android 支持 (HBuilderX 2.8.0+)
* @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器默认statusCode >= 200 && statusCode < 300
*/
constructor(arg = {}) {
if (!isPlainObject(arg)) {
arg = {}
console.warn('设置全局参数必须接收一个Object')
}
this.config = clone({...defaults, ...arg})
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
/**
* @Function
* @param {Request~setConfigCallback} f - 设置全局默认配置
*/
setConfig(f) {
this.config = f(this.config)
}
middleware(config) {
config = mergeConfig(this.config, config)
let chain = [dispatchRequest, undefined]
let promise = Promise.resolve(config)
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected)
})
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected)
})
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise
}
/**
* @Function
* @param {Object} config - 请求配置项
* @prop {String} options.url - 请求路径
* @prop {Object} options.data - 请求参数
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json会尝试对返回的数据做一次 JSON.parse
* @prop {Object} [options.header = config.header] - 请求header
* @prop {Object} [options.method = config.method] - 请求方法
* @returns {Promise<unknown>}
*/
request(config = {}) {
return this.middleware(config)
}
get(url, options = {}) {
return this.middleware({
url,
method: 'GET',
...options
})
}
post(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'POST',
...options
})
}
// #ifndef MP-ALIPAY || MP-KUAISHOU || MP-JD
put(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'PUT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
delete(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'DELETE',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
connect(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'CONNECT',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN || MP-BAIDU
head(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'HEAD',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
options(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'OPTIONS',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
trace(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'TRACE',
...options
})
}
// #endif
upload(url, config = {}) {
config.url = url
config.method = 'UPLOAD'
return this.middleware(config)
}
download(url, config = {}) {
config.url = url
config.method = 'DOWNLOAD'
return this.middleware(config)
}
get version () {
return '3.1.0'
}
}
/**
* setConfig回调
* @return {Object} - 返回操作后的config
* @callback Request~setConfigCallback
* @param {Object} config - 全局默认config
*/

View File

@ -0,0 +1,20 @@
'use strict'
import isAbsoluteURL from '../helpers/isAbsoluteURL'
import combineURLs from '../helpers/combineURLs'
/**
* Creates a new URL by combining the baseURL with the requestedURL,
* only when the requestedURL is not already an absolute URL.
* If the requestURL is absolute, this function returns the requestedURL untouched.
*
* @param {string} baseURL The base URL
* @param {string} requestedURL Absolute or relative URL to combine
* @returns {string} The combined full path
*/
export default function buildFullPath(baseURL, requestedURL) {
if (baseURL && !isAbsoluteURL(requestedURL)) {
return combineURLs(baseURL, requestedURL)
}
return requestedURL
}

View File

@ -0,0 +1,33 @@
/**
* 默认的全局配置
*/
export default {
baseURL: '',
header: {},
method: 'GET',
dataType: 'json',
paramsSerializer: null,
// #ifndef MP-ALIPAY
responseType: 'text',
// #endif
custom: {},
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
timeout: 60000,
// #endif
// #ifdef APP-PLUS
sslVerify: true,
// #endif
// #ifdef H5
withCredentials: false,
// #endif
// #ifdef APP-PLUS
firstIpv4: false,
// #endif
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300
},
// 是否尝试将响应数据json化
forcedJSONParsing: true
}

View File

@ -0,0 +1,6 @@
import adapter from '../adapters/index'
export default (config) => {
return adapter(config)
}

View File

@ -0,0 +1,126 @@
import {deepMerge, isUndefined} from '../utils'
/**
* 合并局部配置优先的配置如果局部有该配置项则用局部如果全局有该配置项则用全局
* @param {Array} keys - 配置项
* @param {Object} globalsConfig - 当前的全局配置
* @param {Object} config2 - 局部配置
* @return {{}}
*/
const mergeKeys = (keys, globalsConfig, config2) => {
let config = {}
keys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
} else if (!isUndefined(globalsConfig[prop])) {
config[prop] = globalsConfig[prop]
}
})
return config
}
/**
*
* @param globalsConfig - 当前实例的全局配置
* @param config2 - 当前的局部配置
* @return - 合并后的配置
*/
export default (globalsConfig, config2 = {}) => {
const method = config2.method || globalsConfig.method || 'GET'
let config = {
baseURL: config2.baseURL || globalsConfig.baseURL || '',
method: method,
url: config2.url || '',
params: config2.params || {},
custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
header: deepMerge(globalsConfig.header || {}, config2.header || {})
}
const defaultToConfig2Keys = ['getTask', 'validateStatus', 'paramsSerializer', 'forcedJSONParsing']
config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
// eslint-disable-next-line no-empty
if (method === 'DOWNLOAD') {
const downloadKeys = [
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
'timeout',
// #endif
// #ifdef MP
'filePath',
// #endif
]
config = {...config, ...mergeKeys(downloadKeys, globalsConfig, config2)}
} else if (method === 'UPLOAD') {
delete config.header['content-type']
delete config.header['Content-Type']
const uploadKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef MP-ALIPAY
'fileType',
// #endif
// #ifdef H5
'file',
// #endif
'filePath',
'name',
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
'timeout',
// #endif
'formData',
]
uploadKeys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
config['timeout'] = globalsConfig['timeout']
}
// #endif
} else {
const defaultsKeys = [
'data',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4',
// #endif
// #ifdef MP-WEIXIN
'enableHttp2',
'enableQuic',
// #endif
// #ifdef MP-TOUTIAO || MP-WEIXIN
'enableCache',
// #endif
// #ifdef MP-WEIXIN
'enableHttpDNS',
'httpDNSServiceId',
'enableChunked',
'forceCellularNetwork',
// #endif
// #ifdef MP-ALIPAY
'enableCookie',
// #endif
// #ifdef MP-BAIDU
'cloudCache',
'defer'
// #endif
]
config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
}
return config
}

View File

@ -0,0 +1,16 @@
/**
* Resolve or reject a Promise based on response status.
*
* @param {Function} resolve A function that resolves the promise.
* @param {Function} reject A function that rejects the promise.
* @param {object} response The response.
*/
export default function settle(resolve, reject, response) {
const validateStatus = response.config.validateStatus
const status = response.statusCode
if (status && (!validateStatus || validateStatus(status))) {
resolve(response)
} else {
reject(response)
}
}

View File

@ -0,0 +1,64 @@
'use strict'
import * as utils from './../utils'
function encode(val) {
return encodeURIComponent(val).replace(/%40/gi, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ',').replace(/%20/g, '+').replace(/%5B/gi, '[').replace(/%5D/gi, ']')
}
/**
* Build a URL by appending params to the end
*
* @param {string} url The base of the url (e.g., http://www.google.com)
* @param {object} [params] The params to be appended
* @returns {string} The formatted url
*/
export default function buildURL(url, params, paramsSerializer) {
/*eslint no-param-reassign:0*/
if (!params) {
return url
}
var serializedParams
if (paramsSerializer) {
serializedParams = paramsSerializer(params)
} else if (utils.isURLSearchParams(params)) {
serializedParams = params.toString()
} else {
var parts = []
utils.forEach(params, function serialize(val, key) {
if (val === null || typeof val === 'undefined') {
return
}
if (utils.isArray(val)) {
key = key + '[]'
} else {
val = [val]
}
utils.forEach(val, function parseValue(v) {
if (utils.isDate(v)) {
v = v.toISOString()
} else if (utils.isObject(v)) {
v = JSON.stringify(v)
}
parts.push(encode(key) + '=' + encode(v))
})
})
serializedParams = parts.join('&')
}
if (serializedParams) {
var hashmarkIndex = url.indexOf('#')
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex)
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}

View File

@ -0,0 +1,14 @@
'use strict'
/**
* Creates a new URL by combining the specified URLs
*
* @param {string} baseURL The base URL
* @param {string} relativeURL The relative URL
* @returns {string} The combined URL
*/
export default function combineURLs(baseURL, relativeURL) {
return relativeURL
? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
: baseURL
}

View File

@ -0,0 +1,14 @@
'use strict'
/**
* Determines whether the specified URL is absolute
*
* @param {string} url The URL to test
* @returns {boolean} True if the specified URL is absolute, otherwise false
*/
export default function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
}

View File

@ -0,0 +1,197 @@
export type HttpTask = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask;
export type HttpRequestTask = UniApp.RequestTask;
export type HttpUploadTask = UniApp.UploadTask;
export type HttpDownloadTask = UniApp.DownloadTask;
export type HttpMethod =
"GET"
| "POST"
| "PUT"
| "DELETE"
| "CONNECT"
| "HEAD"
| "OPTIONS"
| "TRACE"
| "UPLOAD"
| "DOWNLOAD";
export type HttpRequestHeader = Record<string, string>;
export type HttpParams = Record<string, any>;
export type HttpData = Record<string, any>;
export type HttpResponseType = 'arraybuffer' | 'text';
export type HttpCustom = Record<string, any>;
export type HttpFileType = 'image' | 'video' | 'audio';
export type HttpFormData = Record<string, any>;
export type HttpResponseHeader = Record<string, string> & {
"set-cookie"?: string[]
};
export interface HttpRequestConfig<T = HttpTask> {
/** @desc 请求服务器接口地址 */
url?: string;
/** @desc 请求方式,默认为 GET */
method?: HttpMethod;
/** @desc 请求基地址 */
baseURL?: string;
/** @desc 请求头信息,不能设置 RefererApp、H5 端会自动带上 cookie且 H5 端不可手动修改 */
header?: HttpRequestHeader;
/** @desc 请求查询参数,自动拼接为查询字符串 */
params?: HttpParams;
/** @desc 请求体参数 */
data?: HttpData;
/** @desc 超时时间,单位 ms默认为 60000仅 H5 (HBuilderX 2.9.9+)、APP (HBuilderX 2.9.9+)、微信小程序 (2.10.0)、支付宝小程序支持 */
timeout?: number;
/** @desc 跨域请求时是否携带凭证 (cookies),默认为 false仅 H5 (HBuilderX 2.6.15+) 支持 */
withCredentials?: boolean;
/** @desc 设置响应的数据类型,支付宝小程序不支持 */
responseType?: HttpResponseType;
/** @desc 全局自定义验证器 */
validateStatus?: ((statusCode: number) => boolean) | null;
/** params 参数自定义处理 */
paramsSerializer?: (params: AnyObject) => string | void;
/** @desc 默认为 json如果设为 json会尝试对返回的数据做一次 JSON.parse */
dataType?: string;
/** @desc DNS 解析时是否优先使用 ipv4默认为 false仅 App-Android (HBuilderX 2.8.0+) 支持 */
firstIpv4?: boolean;
/** @desc 是否验证 SSL 证书,默认为 true仅 App-Android (HBuilderX 2.3.3+) 支持 */
sslVerify?: boolean;
/** @desc 开启 http2;微信小程序 */
enableHttp2?: boolean;
/** @desc 开启 quic微信小程序 */
enableQuic?: boolean;
/** @desc 开启 cache;微信小程序、字节跳动小程序 2.31.0+ */
enableCache?: boolean;
/** @desc 开启 httpDNS;微信小程序 */
enableHttpDNS?: boolean;
/** @desc httpDNS 服务商;微信小程序 */
httpDNSServiceId?: string;
/** @desc 开启 transfer-encoding chunked;微信小程序 */
enableChunked?: boolean;
/** @desc wifi下使用移动网络发送请求;微信小程序 */
forceCellularNetwork?: boolean;
/** @desc 开启后可在headers中编辑cookie;支付宝小程序 10.2.33+ */
enableCookie?: boolean;
/** @desc 是否开启云加速;百度小程序 3.310.11+ */
cloudCache?: boolean | object;
/** @desc 控制当前请求是否延时至首屏内容渲染后发送;百度小程序 3.310.11+ */
defer?: boolean;
/** @desc 自定义参数 */
custom?: HttpCustom;
/** @desc 返回当前请求的 task 和 options不要在这里修改 options */
getTask?: (task: T, options: HttpRequestConfig<T>) => void;
/** @desc 需要上传的文件列表,使用 files 时filePath 和 name 不生效,仅支持 App、H5 (2.6.15+) */
files?: { name?: string; file?: File; uri: string; }[];
/** @desc 文件类型,仅支付宝小程序支持且为必填项 */
fileType?: HttpFileType;
/** @desc 要上传的文件对象,仅 H5 (2.6.15+) 支持 */
file?: File;
/** @desc 要上传文件资源的路径,使用 files 时filePath 和 name 不生效 */
filePath?: string;
/** @desc 文件对应的 key开发者在服务器端通过这个 key 可以获取到文件二进制内容,使用 files 时filePath 和 name 不生效 */
name?: string;
/** @desc 请求中其他额外的 form data */
formData?: HttpFormData;
}
export interface HttpResponse<T = any, D = HttpTask> {
data: T;
statusCode: number;
header: HttpResponseHeader;
config: HttpRequestConfig<D>;
cookies: string[];
errMsg: string;
rawData: any;
}
export interface HttpUploadResponse<T = any, D = HttpTask> {
data: T;
statusCode: number;
config: HttpRequestConfig<D>;
errMsg: string;
rawData: any;
}
export interface HttpDownloadResponse extends HttpResponse {
tempFilePath: string;
apFilePath?: string;
filePath?: string;
fileContent?: string;
}
export interface HttpError<T = any, D = HttpTask> {
data?: T;
statusCode?: number;
header?: HttpResponseHeader;
config: HttpRequestConfig<D>;
cookies?: string[];
errMsg: string;
}
export interface HttpPromise<T = any> extends Promise<HttpResponse<T>> {
}
export interface HttpInterceptorManager<V, E = V> {
use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: E) => T | Promise<E>): void;
eject(id: number): void;
}
export abstract class HttpRequestAbstract {
constructor(config?: HttpRequestConfig);
interceptors: {
request: HttpInterceptorManager<HttpRequestConfig>;
response: HttpInterceptorManager<HttpResponse, HttpError>;
}
request<T = any, R = HttpResponse<T>, D = HttpRequestTask>(config: HttpRequestConfig<D>): Promise<R>;
get<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
delete<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
head<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
options<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
post<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
put<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
config: HttpRequestConfig;
setConfig<D = HttpTask>(onSend: (config: HttpRequestConfig<D>) => HttpRequestConfig<D>): void;
connect<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
trace<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
upload<T = any, R = HttpUploadResponse<T>, D = HttpUploadTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
download<T = any, R = HttpDownloadResponse<T>, D = HttpDownloadTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
middleware<T = any, R = HttpResponse<T>, D = HttpTask>(config: HttpRequestConfig<D>): Promise<R>;
}
declare class HttpRequest extends HttpRequestAbstract {
}
export default HttpRequest;

View File

@ -0,0 +1,2 @@
import Request from './core/Request'
export default Request

View File

@ -0,0 +1,135 @@
'use strict'
// utils is a library of generic helper functions non-specific to axios
var toString = Object.prototype.toString
/**
* Determine if a value is an Array
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Array, otherwise false
*/
export function isArray (val) {
return toString.call(val) === '[object Array]'
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
export function isObject (val) {
return val !== null && typeof val === 'object'
}
/**
* Determine if a value is a Date
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Date, otherwise false
*/
export function isDate (val) {
return toString.call(val) === '[object Date]'
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
export function isURLSearchParams (val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}
/**
* Iterate over an Array or an Object invoking a function for each item.
*
* If `obj` is an Array callback will be called passing
* the value, index, and complete array for each item.
*
* If 'obj' is an Object callback will be called passing
* the value, key, and complete object for each property.
*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
export function forEach (obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === 'undefined') {
return
}
// Force an array if not already something iterable
if (typeof obj !== 'object') {
/*eslint no-param-reassign:0*/
obj = [obj]
}
if (isArray(obj)) {
// Iterate over array values
for (var i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj)
}
} else {
// Iterate over object keys
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj)
}
}
}
}
/**
* 是否为boolean
* @param val
* @returns {boolean}
*/
export function isBoolean(val) {
return typeof val === 'boolean'
}
/**
* 是否为真正的对象{} new Object
* @param {any} obj - 检测的对象
* @returns {boolean}
*/
export function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
/**
* Function equal to merge with the difference being that no reference
* to original objects is kept.
*
* @see merge
* @param {Object} obj1 Object to merge
* @returns {Object} Result of all merge properties
*/
export function deepMerge(/* obj1, obj2, obj3, ... */) {
let result = {}
function assignValue(val, key) {
if (typeof result[key] === 'object' && typeof val === 'object') {
result[key] = deepMerge(result[key], val)
} else if (typeof val === 'object') {
result[key] = deepMerge({}, val)
} else {
result[key] = val
}
}
for (let i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue)
}
return result
}
export function isUndefined (val) {
return typeof val === 'undefined'
}

View File

@ -0,0 +1,264 @@
/* eslint-disable */
var clone = (function() {
'use strict';
function _instanceof(obj, type) {
return type != null && obj instanceof type;
}
var nativeMap;
try {
nativeMap = Map;
} catch(_) {
// maybe a reference error because no `Map`. Give it a dummy value that no
// value will ever be an instanceof.
nativeMap = function() {};
}
var nativeSet;
try {
nativeSet = Set;
} catch(_) {
nativeSet = function() {};
}
var nativePromise;
try {
nativePromise = Promise;
} catch(_) {
nativePromise = function() {};
}
/**
* Clones (copies) an Object using deep copying.
*
* This function supports circular references by default, but if you are certain
* there are no circular references in your object, you can save some CPU time
* by calling clone(obj, false).
*
* Caution: if `circular` is false and `parent` contains circular references,
* your program may enter an infinite loop and crash.
*
* @param `parent` - the object to be cloned
* @param `circular` - set to true if the object to be cloned may contain
* circular references. (optional - true by default)
* @param `depth` - set to a number if the object is only to be cloned to
* a particular depth. (optional - defaults to Infinity)
* @param `prototype` - sets the prototype to be used when cloning an object.
* (optional - defaults to parent prototype).
* @param `includeNonEnumerable` - set to true if the non-enumerable properties
* should be cloned as well. Non-enumerable properties on the prototype
* chain will be ignored. (optional - false by default)
*/
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
if (typeof circular === 'object') {
depth = circular.depth;
prototype = circular.prototype;
includeNonEnumerable = circular.includeNonEnumerable;
circular = circular.circular;
}
// maintain two arrays for circular references, where corresponding parents
// and children have the same index
var allParents = [];
var allChildren = [];
var useBuffer = typeof Buffer != 'undefined';
if (typeof circular == 'undefined')
circular = true;
if (typeof depth == 'undefined')
depth = Infinity;
// recurse this function so we don't reset allParents and allChildren
function _clone(parent, depth) {
// cloning null always returns null
if (parent === null)
return null;
if (depth === 0)
return parent;
var child;
var proto;
if (typeof parent != 'object') {
return parent;
}
if (_instanceof(parent, nativeMap)) {
child = new nativeMap();
} else if (_instanceof(parent, nativeSet)) {
child = new nativeSet();
} else if (_instanceof(parent, nativePromise)) {
child = new nativePromise(function (resolve, reject) {
parent.then(function(value) {
resolve(_clone(value, depth - 1));
}, function(err) {
reject(_clone(err, depth - 1));
});
});
} else if (clone.__isArray(parent)) {
child = [];
} else if (clone.__isRegExp(parent)) {
child = new RegExp(parent.source, __getRegExpFlags(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (clone.__isDate(parent)) {
child = new Date(parent.getTime());
} else if (useBuffer && Buffer.isBuffer(parent)) {
if (Buffer.from) {
// Node.js >= 5.10.0
child = Buffer.from(parent);
} else {
// Older Node.js versions
child = new Buffer(parent.length);
parent.copy(child);
}
return child;
} else if (_instanceof(parent, Error)) {
child = Object.create(parent);
} else {
if (typeof prototype == 'undefined') {
proto = Object.getPrototypeOf(parent);
child = Object.create(proto);
}
else {
child = Object.create(prototype);
proto = prototype;
}
}
if (circular) {
var index = allParents.indexOf(parent);
if (index != -1) {
return allChildren[index];
}
allParents.push(parent);
allChildren.push(child);
}
if (_instanceof(parent, nativeMap)) {
parent.forEach(function(value, key) {
var keyChild = _clone(key, depth - 1);
var valueChild = _clone(value, depth - 1);
child.set(keyChild, valueChild);
});
}
if (_instanceof(parent, nativeSet)) {
parent.forEach(function(value) {
var entryChild = _clone(value, depth - 1);
child.add(entryChild);
});
}
for (var i in parent) {
var attrs = Object.getOwnPropertyDescriptor(parent, i);
if (attrs) {
child[i] = _clone(parent[i], depth - 1);
}
try {
var objProperty = Object.getOwnPropertyDescriptor(parent, i);
if (objProperty.set === 'undefined') {
// no setter defined. Skip cloning this property
continue;
}
child[i] = _clone(parent[i], depth - 1);
} catch(e){
if (e instanceof TypeError) {
// when in strict mode, TypeError will be thrown if child[i] property only has a getter
// we can't do anything about this, other than inform the user that this property cannot be set.
continue
} else if (e instanceof ReferenceError) {
//this may happen in non strict mode
continue
}
}
}
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(parent);
for (var i = 0; i < symbols.length; i++) {
// Don't need to worry about cloning a symbol because it is a primitive,
// like a number or string.
var symbol = symbols[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
continue;
}
child[symbol] = _clone(parent[symbol], depth - 1);
Object.defineProperty(child, symbol, descriptor);
}
}
if (includeNonEnumerable) {
var allPropertyNames = Object.getOwnPropertyNames(parent);
for (var i = 0; i < allPropertyNames.length; i++) {
var propertyName = allPropertyNames[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
if (descriptor && descriptor.enumerable) {
continue;
}
child[propertyName] = _clone(parent[propertyName], depth - 1);
Object.defineProperty(child, propertyName, descriptor);
}
}
return child;
}
return _clone(parent, depth);
}
/**
* Simple flat clone using prototype, accepts only objects, usefull for property
* override on FLAT configuration object (no nested props).
*
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
* works.
*/
clone.clonePrototype = function clonePrototype(parent) {
if (parent === null)
return null;
var c = function () {};
c.prototype = parent;
return new c();
};
// private utility functions
function __objToStr(o) {
return Object.prototype.toString.call(o);
}
clone.__objToStr = __objToStr;
function __isDate(o) {
return typeof o === 'object' && __objToStr(o) === '[object Date]';
}
clone.__isDate = __isDate;
function __isArray(o) {
return typeof o === 'object' && __objToStr(o) === '[object Array]';
}
clone.__isArray = __isArray;
function __isRegExp(o) {
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
}
clone.__isRegExp = __isRegExp;
function __getRegExpFlags(re) {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
}
clone.__getRegExpFlags = __getRegExpFlags;
return clone;
})();
export default clone

View File

@ -0,0 +1,20 @@
{
"id": "luch-request",
"name": "luch-request",
"displayName": "luch-request",
"version": "3.1.0",
"description": "基于Promise开发的跨平台、项目级别的请求库它有更小的体积易用的api方便简单的自定义能力",
"keywords": [
"request",
"axios",
"网络请求",
"拦截器",
"promise"
],
"dcloudext": {
"category": [
"JS SDK",
"通用 SDK"
]
}
}

124
src/locale/en.json Normal file
View File

@ -0,0 +1,124 @@
{
"common": {
"readMore": "READ MORE",
"viewDetails": "view details",
"truckModel": "Truck Model",
"dimension": "Dimension (LxWxH)(mm)",
"overhang": "Overhang (front/rear)(mm)",
"wheelBase": "Wheelbase (mm)",
"maxSpeed": "Max speed (km/h)",
"curbWeight": "Curb weight (kg)",
"loadingWeight": "Loading weight (kg)",
"approachingAngle/Departure": "Approaching angle/Departure angle (º)",
"products": "PRODUCTS",
"companyProducts": "COMPANY PRODUCTS",
"brand": "Brand",
"driveWheel": "Drive Wheel",
"drivingStyle": "Driving Style",
"placeofOrigin": "Place of Origin",
"tel": "Tel",
"Mob/Whatsapp/Wechat": "Mob/Whatsapp/Wechat",
"E-mail": "E-mail",
"send": "SEND",
"enterEail": "Enter your email here",
"addressLabel": "Address",
"fax": "Fax",
"submit": "SUBMIT",
"sendInquiry": "Send Inquiry",
"chatNow": "Chat Now",
"menu": "Menu",
"search": "Search",
"next": "Next",
"prev": "Prev",
"submitSuccess": "Submit success",
"fulltextsearch": "Full text search",
"language": "Language",
"Categories": "Categories",
"empty": "No data available at the moment",
"Contact": "Contact",
"Online": "Online",
"Whats": "Whats",
"TOP": "TOP"
},
"menu": {
"Home": "Home",
"AboutUs": "AboutUs",
"Products": "Products",
"News": "News",
"Exhibition": "Exhibition",
"ContactUs": "ContactUs",
"Feedback": "Feedback"
},
"news": {
"news": "News",
"newsCenter": "NEWS CENTER",
"allNews": "ALL NEWS",
"relatedNews": "Related News",
"relatedProducts": "Related Products"
},
"exhibition": {
"exhibition": "Exhibition",
"exhibitionCenter": "EXHIBITION CENTER",
"allExhibition": "ALL EXHIBITION",
"relatedExhibition": "Related Exhibition",
"relatedProducts": "Related Products"
},
"products": {
"products": "PRODUCTS",
"allProducts": "ALL PRODUCTS",
"productDetails": "Product Details",
"inquiry": "Inquiry"
},
"index": {
"companyProfile": "COMPANY PROFILE",
"corporateName": "SINOTRUCK HOWO SALES CO.,LTD.",
"companyProfileContent": "SINOTRUCK HOWO SALES CO.,LTD.is located in Jinan city, Shandong Province, P.R. China. It is the cradle and leading production base of the heavy-duty truck industry in China. It is famous for developing and manufacturing SINOTRUK HOWO tractor trucks, dump trucks, fuel tanker trucks and concrete mixer truck, water spraying trucks etc.",
"highQuality": "HIGH QUALITY",
"LEadingbBraNd": "LEADING BRAND",
"experiencedStaff": "EXPERIENCED STAFF",
"advancedEquipment": "ADVANCED EQUIPMENT",
"popularProduct": "POPULAR PRODUCT",
"productDesc": "The company's popular products are on display. Welcome to purchase",
"pressCenter": "PRESS CENTER",
"realTimeNewsReadingOnline": "Real-time news reading online",
"joinOurNewsletter": "JOIN OUR NEWSLETTER",
"signUpOurNewsletter": "Sign up our newsletter and get more events & promotions!"
},
"aboutUs": {
"AboutUs": "About Us",
"company": "Sinotruck Howo Sales Co.,Ltd",
"address": "Room 2403, No.1 Yinzuo Jingdu Mansion, Shunhe East Street No.66, District Shizhong, Jinan city, shandong province, China"
},
"contactUs": {
"contactUs": "Contact Us",
"contacts": "Alice",
"way": "Find us through some contact information",
"contact": "Contact",
"name": "Your name",
"title": "Title",
"company": "Company",
"mail": "Mail",
"contactWay": "Phone/WhatsApp",
"desc": "Please fill in the form with details so we can send you a quotation correctly",
"address": "Shangang xintiandi No8, 19 floor, Lixia district, jinan, Shandong, China"
},
"feedback": {
"feedback": "Feedback",
"way": "Find us through some contact information"
},
"footer": {
"contactUs": "Contact us"
},
"seo": {
"news": {
"title": "China National Heavy Duty Truck Group Co., Ltd. - News List",
"keywords": "China National Heavy Duty Truck Group (CNHTC) HOWO, HOWO Trucks, Heavy Truck Sales, Commercial Vehicles, Heavy duty Trucks, HOWO News, CNHTC News, Truck Trading, HOWO After sales Service, Large Trucks",
"description": "China National Heavy Duty Truck Group Co., Ltd. provides the latest news, product information, and industry trends on HOWO trucks, focusing on heavy-duty truck sales and services. Understand the information of HOWO models, grasp the trends of the heavy-duty truck industry, and help you make wise purchasing decisions."
},
"product": {
"title": "China National Heavy Duty Truck Group HOWO Product Display - HOWO Truck Series",
"keywords": "China National Heavy Duty Truck Group (CNHTC) HOWO, HOWO Trucks, Heavy Truck Products, Heavy duty Trucks, Commercial Vehicles, HOWO Models, CNHTC Products, HOWO Truck Series, Large Trucks, Commercial Vehicle Products",
"description": "China National Heavy Duty Truck Group Co., Ltd. provides a rich range of HOWO truck products, including various models and configurations. Understand the performance, technical specifications, and industry advantages of our heavy-duty trucks to meet your procurement needs."
}
}
}

124
src/locale/zh.json Normal file
View File

@ -0,0 +1,124 @@
{
"common": {
"readMore": "阅读更多",
"viewDetails": "查看详情",
"truckModel": "卡车型号",
"dimension": "尺寸长x宽x高毫米",
"overhang": "悬垂(前/后mm",
"wheelBase": "轴距mm",
"maxSpeed": "最高速度(公里/小时)",
"curbWeight": "整备质量kg",
"loadingWeight": "装载重量kg",
"approachingAngle/Departure": "接近角/离开角(º)",
"products": "产品",
"companyProducts": "公司产品",
"brand": "品牌",
"driveWheel": "驱动轮",
"drivingStyle": "驾驶方式",
"placeofOrigin": "产地",
"tel": "电话",
"Mob/Whatsapp/Wechat": "手机/Whatsapp/微信",
"E-mail": "电子邮件",
"send": "发送",
"enterEail": "在此处输入您的电子邮件",
"addressLabel": "地址",
"fax": "传真",
"submit": "提交",
"sendInquiry": "发送咨询",
"chatNow": "立即聊天",
"menu": "菜单",
"search": "搜索",
"next": "下一个",
"prev": "上一个",
"submitSuccess": "提交成功",
"fulltextsearch": "全站搜索",
"language": "语言",
"Categories": "分类",
"empty": "暂无数据",
"Contact": "联系我们",
"Online": "在线",
"Whats": "Whatsapp",
"TOP": "顶部"
},
"menu": {
"Home": "首页",
"AboutUs": "关于我们",
"Products": "产品",
"News": "新闻",
"Exhibition": "展览",
"ContactUs": "联系我们",
"Feedback": "反馈"
},
"index": {
"companyProfile": "公司简介",
"corporateName": "中国重汽豪沃销售有限公司",
"companyProfileContent": "中国重汽豪沃销售有限公司位于中国山东省济南市。它是中国重型卡车行业的摇篮和领先的生产基地。以开发和制造中国重汽豪沃牵引车、自卸卡车、油罐车和混凝土搅拌车、洒水车等而闻名。",
"highQuality": "高品质",
"LEadingbBraNd": "领先品牌",
"experiencedStaff": "经验丰富的员工",
"advancedEquipment": "先进设备",
"popularProduct": "热门产品",
"productDesc": "该公司的热门产品正在展出。欢迎购买",
"pressCenter": "新闻中心",
"realTimeNewsReadingOnline": "实时新闻在线阅读",
"joinOurNewsletter": "加入我们的实时通讯",
"signUpOurNewsletter": "注册我们的实时通讯,获取更多活动和促销!"
},
"news": {
"news": "新闻",
"newsCenter": "新闻中心",
"allNews": "所有新闻",
"relatedNews": "相关新闻",
"relatedProducts": "相关产品"
},
"exhibition": {
"exhibition": "展览",
"exhibitionCenter": "展览中心",
"allExhibition": "所有展览",
"relatedExhibition": "相关展览",
"relatedProducts": "相关产品"
},
"products": {
"products": "产品",
"allProducts": "所有产品",
"productDetails": "产品详情",
"inquiry": "询价"
},
"aboutUs": {
"AboutUs": "关于我们",
"company": "中车豪沃销售有限公司",
"address": "中国山东省济南市市中区顺河东街66号银座京都大厦1号2403室"
},
"contactUs": {
"contactUs": "联系我们",
"contacts": "艾丽斯",
"way": "通过一些联系方式找到我们",
"contact": "联系",
"name": "你的名字",
"title": "你的职位",
"company": "你的公司",
"mail": "邮件",
"contactWay": "电话/WhatsApp",
"desc": "请详细填写表格,以便我们正确地向您发送报价",
"address": "山东省济南市历下区19楼善港新天地8号"
},
"feedback": {
"feedback": "反馈",
"way": "通过一些联系方式找到我们"
},
"footer": {
"contactUs": "联系我们"
},
"seo": {
"news": {
"title": "中国重汽豪沃销售有限公司 - 新闻列表",
"keywords": "中国重汽豪沃, 豪沃卡车, 重卡销售, 商用车, 重型卡车, 豪沃新闻, 重汽新闻, 卡车交易, 豪沃售后服务, 大型卡车",
"description": "中国重汽豪沃销售有限公司提供最新的豪沃卡车新闻、产品信息及行业动态,专注于重卡销售与服务。了解豪沃车型资讯,掌握重型卡车行业趋势,助您做出明智的采购决策。"
},
"product": {
"title": "中国重汽豪沃产品展示 - 豪沃卡车系列",
"keywords": "中国重汽豪沃, 豪沃卡车, 重卡产品, 重型卡车, 商用车, 豪沃车型, 重汽产品, 豪沃卡车系列, 大型卡车, 商用车产品",
"description": "中国重汽豪沃销售有限公司提供丰富的豪沃卡车产品系列,包括各种车型和配置。了解我们的重型卡车性能、技术参数及行业优势,满足您的采购需求。"
}
}
}

43
src/main.js Normal file
View File

@ -0,0 +1,43 @@
import { createSSRApp,provide,h } from "vue";
import App from "./App.vue";
import { createPinia } from 'pinia';
import { createI18n } from 'vue-i18n';
import zh from './locale/zh.json';
import en from './locale/en.json';
import ConfirmPopup from '@/components/comfirm-popup.vue'; // 全局统一的操作弹窗
uni.$showTost = function (title='加载失败!',duration=1500,mask=false) {
// 变成异步代码,处理一些特殊页面 必须等待消息提示结束后在做操作
return new Promise((res,rej) => {
uni.showToast({
title,
mask,
icon:'none',
duration
})
setTimeout(() => {
res(true)
},duration)
})
}
const messages = {
en,
zh,
}
let i18nConfig = {
locale: uni.getLocale(),// 获取已设置的语言
messages
}
const i18n = createI18n(i18nConfig)
export function createApp() {
const app = createSSRApp(App);
app.use(i18n)
app.use(createPinia());
app.component('ConfirmPopup', ConfirmPopup)
return {
app,
};
}

85
src/manifest.json Normal file
View File

@ -0,0 +1,85 @@
{
"name" : "HOWO",
"appid" : "__UNI__DADC1D2",
"description" : "Sinotruck Howo Sales Co.,Ltd is one of the most professional trailer, truck, trailer parts, truck parts manufacturers in China. Our factory brings here a great selection of truck for sale. Welcome to consult price with us.",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"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\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx4cb7d04123386d0e",
"setting" : {
"urlCheck" : false,
"postcss" : true,
"es6" : true,
"minified" : true
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3",
"h5" : {
"router" : {
"mode" : "history"
},
"devServer" : {
"port" : 5173
},
"title" : "China Trailer, Truck, Trailer Parts, Truck Parts Manufacturers and Factory - Price - SINOTRUCK"
},
"locale" : "en"
}

24
src/pages.json Normal file
View File

@ -0,0 +1,24 @@
{
"easycom": {
"autoscan": true,
"custom": {
// uni-ui
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "China Trailer, Truck, Trailer Parts, Truck Parts Manufacturers and Factory - Price - SINOTRUCK"
}
}
],
"globalStyle": {
"navigationStyle": "custom",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "China Trailer, Truck, Trailer Parts, Truck Parts Manufacturers and Factory - Price - SINOTRUCK",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}

25
src/pages/index/index.vue Normal file
View File

@ -0,0 +1,25 @@
<template>
<view class="content">
{{$t('index.companyProfile')}}
<ConfirmPopup ref="comfirmPopupRef" v-if="popupShow" />
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { index } from '@/stores/index.js';
import { useComfirmPopup } from '@/hooks/utils.js';
import { getFriendLink } from '@/api/index.js';
const { popupShow,comfirmPopupRef,openPopup } = useComfirmPopup()
const piniaIndex = index()
onLoad(() => {
})
</script>
<style lang="scss" scoped>
</style>

BIN
src/static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

14
src/stores/index.js Normal file
View File

@ -0,0 +1,14 @@
import { defineStore } from 'pinia';
import { reactive, ref } from 'vue';
export const index = defineStore('index',()=>{
let num = ref(0);
const countHanderl = () => {
num.value += 1
}
return {
num,
countHanderl
}
})

76
src/uni.scss Normal file
View File

@ -0,0 +1,76 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color: #333; // 基本色
$uni-text-color-inverse: #fff; // 反色
$uni-text-color-grey: #999; // 辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #fff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; // 点击状态颜色
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色
/* 边框颜色 */
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16;
/* 图片尺寸 */
$uni-img-size-sm: 20px;
$uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 20px;
$uni-color-subtitle: #555; // 二级标题颜色
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;

352
src/utils/html-parser.js Normal file
View File

@ -0,0 +1,352 @@
/*
* HTML5 Parser By Sam Blowes
*
* Designed for HTML5 documents
*
* Original code by John Resig (ejohn.org)
* http://ejohn.org/blog/pure-javascript-html-parser/
* Original code by Erik Arvidsson, Mozilla Public License
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
*
* ----------------------------------------------------------------------------
* License
* ----------------------------------------------------------------------------
*
* This code is triple licensed using Apache Software License 2.0,
* Mozilla Public License or GNU Public License
*
* ////////////////////////////////////////////////////////////////////////////
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* ////////////////////////////////////////////////////////////////////////////
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Simple HTML Parser.
*
* The Initial Developer of the Original Code is Erik Arvidsson.
* Portions created by Erik Arvidssson are Copyright (C) 2004. All Rights
* Reserved.
*
* ////////////////////////////////////////////////////////////////////////////
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ----------------------------------------------------------------------------
* Usage
* ----------------------------------------------------------------------------
*
* // Use like so:
* HTMLParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* // or to get an XML string:
* HTMLtoXML(htmlString);
*
* // or to get an XML DOM Document
* HTMLtoDOM(htmlString);
*
* // or to inject into an existing document/DOM node
* HTMLtoDOM(htmlString, document);
* HTMLtoDOM(htmlString, document.body);
*
*/
// Regular Expressions for parsing tags and attributes
var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
var endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/;
var attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; // Empty Elements - HTML 5
var empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); // Block Elements - HTML 5
// fixed by xxx 将 ins 标签从块级名单中移除
var block = makeMap('a,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); // Inline Elements - HTML 5
var inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); // Elements that you can, intentionally, leave open
// (and which close themselves)
var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); // Attributes that have their values filled in disabled="disabled"
var fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); // Special Elements (can contain anything)
var special = makeMap('script,style');
function HTMLParser(html, handler) {
var index;
var chars;
var match;
var stack = [];
var last = html;
stack.last = function () {
return this[this.length - 1];
};
while (html) {
chars = true; // Make sure we're not in a script or style element
if (!stack.last() || !special[stack.last()]) {
// Comment
if (html.indexOf('<!--') == 0) {
index = html.indexOf('-->');
if (index >= 0) {
if (handler.comment) {
handler.comment(html.substring(4, index));
}
html = html.substring(index + 3);
chars = false;
} // end tag
} else if (html.indexOf('</') == 0) {
match = html.match(endTag);
if (match) {
html = html.substring(match[0].length);
match[0].replace(endTag, parseEndTag);
chars = false;
} // start tag
} else if (html.indexOf('<') == 0) {
match = html.match(startTag);
if (match) {
html = html.substring(match[0].length);
match[0].replace(startTag, parseStartTag);
chars = false;
}
}
if (chars) {
index = html.indexOf('<');
var text = index < 0 ? html : html.substring(0, index);
html = index < 0 ? '' : html.substring(index);
if (handler.chars) {
handler.chars(text);
}
}
} else {
html = html.replace(new RegExp('([\\s\\S]*?)<\/' + stack.last() + '[^>]*>'), function (all, text) {
text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, '$1$2');
if (handler.chars) {
handler.chars(text);
}
return '';
});
parseEndTag('', stack.last());
}
if (html == last) {
throw 'Parse Error: ' + html;
}
last = html;
} // Clean up any remaining tags
parseEndTag();
function parseStartTag(tag, tagName, rest, unary) {
tagName = tagName.toLowerCase();
if (block[tagName]) {
while (stack.last() && inline[stack.last()]) {
parseEndTag('', stack.last());
}
}
if (closeSelf[tagName] && stack.last() == tagName) {
parseEndTag('', tagName);
}
unary = empty[tagName] || !!unary;
if (!unary) {
stack.push(tagName);
}
if (handler.start) {
var attrs = [];
rest.replace(attr, function (match, name) {
var value = arguments[2] ? arguments[2] : arguments[3] ? arguments[3] : arguments[4] ? arguments[4] : fillAttrs[name] ? name : '';
attrs.push({
name: name,
value: value,
escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') // "
});
});
if (handler.start) {
handler.start(tagName, attrs, unary);
}
}
}
function parseEndTag(tag, tagName) {
// If no tag name is provided, clean shop
if (!tagName) {
var pos = 0;
} // Find the closest opened tag of the same type
else {
for (var pos = stack.length - 1; pos >= 0; pos--) {
if (stack[pos] == tagName) {
break;
}
}
}
if (pos >= 0) {
// Close all the open elements, up the stack
for (var i = stack.length - 1; i >= pos; i--) {
if (handler.end) {
handler.end(stack[i]);
}
} // Remove the open elements from the stack
stack.length = pos;
}
}
}
function makeMap(str) {
var obj = {};
var items = str.split(',');
for (var i = 0; i < items.length; i++) {
obj[items[i]] = true;
}
return obj;
}
function removeDOCTYPE(html) {
return html.replace(/<\?xml.*\?>\n/, '').replace(/<!doctype.*>\n/, '').replace(/<!DOCTYPE.*>\n/, '');
}
function parseAttrs(attrs) {
return attrs.reduce(function (pre, attr) {
var value = attr.value;
var name = attr.name;
if (pre[name]) {
pre[name] = pre[name] + " " + value;
} else {
pre[name] = value;
}
return pre;
}, {});
}
function parseHtml(html) {
html = removeDOCTYPE(html);
var stacks = [];
var results = {
node: 'root',
children: []
};
HTMLParser(html, {
start: function start(tag, attrs, unary) {
var node = {
name: tag
};
if (attrs.length !== 0) {
node.attrs = parseAttrs(attrs);
}
if (unary) {
var parent = stacks[0] || results;
if (!parent.children) {
parent.children = [];
}
parent.children.push(node);
} else {
stacks.unshift(node);
}
},
end: function end(tag) {
var node = stacks.shift();
if (node.name !== tag) console.error('invalid state: mismatch end tag');
if (stacks.length === 0) {
results.children.push(node);
} else {
var parent = stacks[0];
if (!parent.children) {
parent.children = [];
}
parent.children.push(node);
}
},
chars: function chars(text) {
var node = {
type: 'text',
text: text
};
if (stacks.length === 0) {
results.children.push(node);
} else {
var parent = stacks[0];
if (!parent.children) {
parent.children = [];
}
parent.children.push(node);
}
},
comment: function comment(text) {
var node = {
node: 'comment',
text: text
};
var parent = stacks[0];
if (!parent.children) {
parent.children = [];
}
parent.children.push(node);
}
});
return results.children;
}
export default parseHtml;

49
src/utils/request.js Normal file
View File

@ -0,0 +1,49 @@
import Request from '@/js_sdk/luch-request/luch-request/index.js';
let $http = new Request()
$http.config = {
baseURL: import.meta.env.VITE_APP_BASE_API
}
// 请求拦截器
// web运行端口默认5173可在manifest进行修改默认端口号
$http.interceptors.request.use(async (config) => { // 可使用async await 做异步操作
config.header = {
...config.header,
'Token': uni.getStorageSync('annualTicketToken'),
}
uni.showLoading({
title: '加载中...',
mask: true
})
return config
}, config => { // 可使用async await 做异步操作
return Promise.reject(config)
})
// 响应拦截器
$http.interceptors.response.use((response) => {
uni.hideLoading()
switch(response.data.code) {
case 0:
break;
default:
// 加入到messageBoxUrl的请求url可触发弹窗形式的提示
let messageBoxUrl = []
if (messageBoxUrl.indexOf(response.config.url) !== -1) {
uni.$showModal('提示',response.data.msg)
} else {
if (response.data.msg) {
uni.$showTost(response.data.msg)
}
}
return Promise.reject(response)
}
return response
}, (response) => {
return Promise.reject(response)
})
export default $http

87
src/utils/tool.js Normal file
View File

@ -0,0 +1,87 @@
/**
* @function 获取uuid
* @return {string} 返回uuid
**/
export const getUuid = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
})
}
// 实现rich-text富文本中图片宽度最大100%
export function formatContent(html) {
// 去掉img标签里的style、width、height属性
let content_data = html.replace(/<img[^>]*>/gi, function(match, capture) {
match = match.replace(/style="[^"]+"/gi, '').replace(/style='[^']+'/gi, '');
match = match.replace(/width="[^"]+"/gi, '').replace(/width='[^']+'/gi, '');
match = match.replace(/height="[^"]+"/gi, '').replace(/height='[^']+'/gi, '');
return match;
});
// 修改所有style里的width属性为max-width:100%
content_data = content_data.replace(/style="[^"]+"/gi, function(match, capture) {
match = match.replace(/width:[^;]+;/gi, 'max-width:100%;').replace(/width:[^;]+;/gi, 'max-width:100%;');
return match;
});
// 去掉<br/>标签
content_data = content_data.replace(/<br[^>]*\/>/gi, '');
// img标签添加style属性max-width:100%;height:auto
content_data = content_data.replace(/\<img/gi,
'<img style="max-width:100%;height:auto;display:block;margin:0px auto;"');
return content_data;
}
/**
* @function 保存图片
* @param {string} url 下载地址
**/
export const downloadFile = (url, type='img') => {
if (!url) {
return uni.$showTost('请传入下载地址!')
}
url = url.replace(/http:\/\//g, 'https://')
uni.showLoading({
title: '保存中...',
mask: true
})
uni.downloadFile({
url: url,
success: async (res) => {
if (type==='img') {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.$showTost('保存成功!')
},
fail: (err) => {
uni.$showTost('保存失败,请手动截屏保存!')
},
complete: () => {
uni.hideLoading()
}
})
} else {
await uni.$showTost('即将打开文件,点击文件预览下拉保存文件!', 2000)
uni.openDocument({
filePath: res.tempFilePath,
fileType: 'pdf',
complete: () => {
uni.hideLoading()
},
fail:() => {
uni.setClipboardData({
data: url,
success: () => {
uni.$showTost('已复制到系统剪切板,请前往浏览器进行下载')
}
})
}
})
}
},
fail: err => {
uni.hideLoading()
uni.$showTost('保存失败')
}
})
}

29
vite.config.js Normal file
View File

@ -0,0 +1,29 @@
import uni from "@dcloudio/vite-plugin-uni";
import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ command, mode }) => {
// 根据当前工作目录中的 `mode` 加载 .env 文件
// 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。
const env = loadEnv(mode, process.cwd(), '')
return {
// vite 配置
plugins: [uni()],
base: './',
define: {
__APP_ENV__: env.APP_ENV,
MY_APP_CONFIG: {
},
},
// 产生跨域请求打开
// server: {
// proxy: {
// '/dev': {
// target: '', // 代理地址
// changeOrigin: true, //支持跨域
// rewrite: (path) => path.replace(/^\/dev/, ""),
// }
// }
// }
}
})