chore: 🚀 init

This commit is contained in:
xuqingkai 2023-06-12 00:24:39 +08:00
parent 9f925396c8
commit 69a4ef5540
402 changed files with 40624 additions and 18 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
babel.config.js
src/uni_modules/mp-html/*

42
.eslintrc.js Normal file
View File

@ -0,0 +1,42 @@
/*
* @Author: weisheng
* @Date: 2023-03-14 16:06:21
* @LastEditTime: 2023-03-28 16:52:32
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\.eslintrc.js
* 记得注释
*/
module.exports = {
env: {
browser: true,
es2021: true
},
extends: ['eslint:recommended', 'plugin:vue/vue3-essential', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
overrides: [],
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020
},
plugins: ['vue', '@typescript-eslint'],
rules: {
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'never'],
'no-console': 'off',
'no-debugger': 'off',
'no-undef': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-namespace': 'off',
'no-inner-declarations': 'off',
'@typescript-eslint/no-this-alias': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/multi-word-component-names': 'off'
}
}

70
.git-cz.json Normal file
View File

@ -0,0 +1,70 @@
{
"disableEmoji": false,
"list": ["test", "feat", "fix", "chore", "docs", "refactor", "style", "ci", "perf", "release", "revert", "build"],
"maxMessageLength": 64,
"minMessageLength": 3,
"questions": ["type", "scope", "subject", "body", "breaking", "issues", "lerna"],
"scopes": [],
"types": {
"chore": {
"description": "Chore | 构建/工程依赖/工具",
"emoji": "🚀",
"value": "chore"
},
"ci": {
"description": "Continuous Integration | CI 配置",
"emoji": "👷",
"value": "ci"
},
"docs": {
"description": "Documentation | 文档",
"emoji": "✏️ ",
"value": "docs"
},
"feat": {
"description": "Features | 新功能",
"emoji": "✨",
"value": "feat"
},
"fix": {
"description": "Bug Fixes | Bug 修复",
"emoji": "🐛",
"value": "fix"
},
"perf": {
"description": "Performance Improvements | 性能优化",
"emoji": "⚡",
"value": "perf"
},
"refactor": {
"description": "Code Refactoring | 代码重构",
"emoji": "♻️ ",
"value": "refactor"
},
"release": {
"description": "Create a release commit | 发版提交",
"emoji": "🏹",
"value": "release"
},
"style": {
"description": "Styles | 风格",
"emoji": "💄",
"value": "style"
},
"revert": {
"description": "Revert | 回退",
"emoji": "⏪",
"value": "revert"
},
"build": {
"description": "Build System | 打包构建",
"emoji": "📦",
"value": "build"
},
"test": {
"description": "Tests | 测试",
"emoji": "✅",
"value": "test"
}
}
}

42
.gitignore vendored
View File

@ -1,18 +1,30 @@
# Build and Release Folders
bin-debug/
bin-release/
[Oo]bj/
[Bb]in/
.DS_Store
node_modules/
unpackage/
dist/
lib/
website/
/docs
.temp
.cache
# src/uni_modules/fant-mini-plus/components/hd-*/API.md
docs/components/hd-*
docs/.vuepress/public/*.zip
# Other files and folders
.settings/
# local env files
.env.local
.env.*.local
# Executables
*.swf
*.air
*.ipa
*.apk
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
# should NOT be excluded as they contain compiler settings and other important
# information for Eclipse / Flash Builder.
# Editor directories and files
.project
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

4
.husky/commit-msg Normal file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx commitlint --edit $1

4
.husky/pre-commit Normal file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged --allow-empty $1

11
.prettierrc Normal file
View File

@ -0,0 +1,11 @@
{
"printWidth": 150,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"requirePragma": false,
"proseWrap": "preserve",
"arrowParens": "always",
"htmlWhitespaceSensitivity": "ignore"
}

39
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,39 @@
{
"workbench.settings.useSplitJSON": true,
// vscodetabsize
"editor.detectIndentation": false,
// tabsize
"editor.tabSize": 2,
// #
// "editor.formatOnSave": true,
// #eslint
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
// vue
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"vue"
],
// #
"prettier.semi": true,
// #使
"prettier.singleQuote": true,
// #()
"javascript.format.insertSpaceBeforeFunctionParenthesis": true,
// #
"vetur.format.defaultFormatter.html": "js-beautify-html",
// #vuejsts
"vetur.format.defaultFormatter.js": "vscode-typescript",
"vetur.format.options.tabSize": 2,
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap_line_length": 150,
"wrap_attributes": true,
"end_with_newline": true
// #vuehtml
}
}
}

112
CHANGELOG.md Normal file
View File

@ -0,0 +1,112 @@
# Changelog
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.0.12](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.11...v0.0.12) (2023-05-16)
### Features
* ✨ Transition组件在APP、微信、H5、支付宝、QQ平台使用wxs优化动画表现 ([14806e0](https://gitlab.hd123.com/vue/fant-mini-plus/commit/14806e0d77ef5e0bf0e93d723861ef306c8c8b3e))
### Bug Fixes
* 🐛 修复Popup组件从中心弹出时宽度异常的问题 ([4fd41d4](https://gitlab.hd123.com/vue/fant-mini-plus/commit/4fd41d4b42c37cce1c939c57e6001d5b3afeafaf))
### [0.0.11](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.10...v0.0.11) (2023-05-15)
### Features
* ✨ Button组件自定义节点设置为虚拟节点去掉微信小程序自定义组件多出的最外层标签 ([a877049](https://gitlab.hd123.com/vue/fant-mini-plus/commit/a87704996224269a9077ee380be72268c936cf82))
### [0.0.10](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.9...v0.0.10) (2023-05-15)
### Features
* ✨ 新增 Image 和 WaterMark 组件 ([90a097e](https://gitlab.hd123.com/vue/fant-mini-plus/commit/90a097e83a7f612f9b76425e07de5f602ff39d3c))
* ✨ 新增WaterMark水印组件 ([37a3979](https://gitlab.hd123.com/vue/fant-mini-plus/commit/37a39798f555d55394d33830256398452d5e7d35))
### [0.0.9](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.8...v0.0.9) (2023-04-25)
### Features
* ✨ Table组件优化支持data-source响应式更新 ([4f4b451](https://gitlab.hd123.com/vue/fant-mini-plus/commit/4f4b4510f395f02f554e45958c40a10cde07ff93))
### Bug Fixes
* 🐛 修复Calendar组件在某些版本uni-app的H5端第一次打开无数据渲染的问题 ([2d2dd33](https://gitlab.hd123.com/vue/fant-mini-plus/commit/2d2dd337c2e29b774c8dedfa01917758b1283917))
### [0.0.8](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.7...v0.0.8) (2023-04-23)
### Features
* ✨ 新增WaterMark水印组件 ([3d9ba3f](https://gitlab.hd123.com/vue/fant-mini-plus/commit/3d9ba3f658c2ce8de8eaec2a5c8bf64963e4f871))
* ✨ Table表格组件支持自定义列模板并增加row-height行高属性 ([23866e2](https://gitlab.hd123.com/vue/fant-mini-plus/commit/23866e2f6ef8ef9a080824f3ebe720dd9755dca6))
### [0.0.7](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.6...v0.0.7) (2023-04-10)
### Bug Fixes
* 🐛 修复README中缺少演示小程序二维码的问题 ([298a21d](https://gitlab.hd123.com/vue/fant-mini-plus/commit/298a21d647bc82d705d4458aa4694ecff35f3f56))
### [0.0.6](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.5...v0.0.6) (2023-04-07)
### Bug Fixes
* 🐛 修复DatePicker组件动画效果生硬的问题 ([1b821e0](https://gitlab.hd123.com/vue/fant-mini-plus/commit/1b821e04e56025e9cee82859e8278dfabfc90d23))
### [0.0.5](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.4...v0.0.5) (2023-04-05)
### Bug Fixes
* 🐛 修复支付宝小程序Calendar组件不显示二级标题月份的问题 ([b7c541d](https://gitlab.hd123.com/vue/fant-mini-plus/commit/b7c541d4b6fbbe10c746f7a56dbc36c5eb1e0831))
* 🐛 修复Area组件有默认值时打开未滚动到默认选项的问题 ([b3df205](https://gitlab.hd123.com/vue/fant-mini-plus/commit/b3df20514b7ef22ef7cb46907229dc9d981a374f))
* 🐛 修复Transition组件动画抖动的问题 ([e5fa79b](https://gitlab.hd123.com/vue/fant-mini-plus/commit/e5fa79b8ee599cc14392abb89cbf988e07104e92))
### [0.0.4](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.3...v0.0.4) (2023-04-04)
### Features
* ✨ Popup和Transition组件新增destory属性用于控制是否销毁插槽中的内容 ([bb26d31](https://gitlab.hd123.com/vue/fant-mini-plus/commit/bb26d318af7af1cffe6d3d9eca9018c1c1ce8f40))
### Bug Fixes
* 🐛 修复Modal组件弹出异常的问题 ([38b88b6](https://gitlab.hd123.com/vue/fant-mini-plus/commit/38b88b6f5fc8e596411ab43df59a1f41db430528))
* 🐛 修复Popup连续多次弹出有概率无法再弹出的问题 ([23e51d2](https://gitlab.hd123.com/vue/fant-mini-plus/commit/23e51d2e04bf6931bd8c3cd5b4dcf7dce15f3850))
### [0.0.3](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.2...v0.0.3) (2023-04-03)
### Bug Fixes
* 🐛 修复DatePicker组件在支付宝平台显示异常的问题 ([604277c](https://gitlab.hd123.com/vue/fant-mini-plus/commit/604277c16a284b05829353a93a6a213f42269dc4))
### [0.0.2](https://gitlab.hd123.com/vue/fant-mini-plus/compare/v0.0.1...v0.0.2) (2023-04-03)
### Bug Fixes
* 🐛 改善transition动画延迟的问题 ([38b4d74](https://gitlab.hd123.com/vue/fant-mini-plus/commit/38b4d74bf27166d81f799661b058762caf6c145c))
### 0.0.1 (2023-03-29)
### Features
* ✨ v0.0.1版本完成移植所有fant-mini组件到vue3 ([e0a7e75](https://gitlab.hd123.com/vue/fant-mini-plus/commit/e0a7e75ffbcc89928be1868d1130cf0a74727882))
### Bug Fixes
* 🐛 修复动画组件在微信小程序上卡顿的问题 ([4c66ced](https://gitlab.hd123.com/vue/fant-mini-plus/commit/4c66cedcb67515a1c1301bba65a9c7e653885df7))

22
build/changelog.js Normal file
View File

@ -0,0 +1,22 @@
/*
* @Author: weisheng
* @Date: 2022-02-24 15:37:04
* @LastEditTime: 2023-03-21 20:57:36
* @LastEditors: weisheng
* @Description: 讲生成的changelog移动到文档和组件中
* @FilePath: \fant-mini-plus-plus\build\changelog.js
* 记得注释
*/
const fs = require('fs')
const path = require('path')
const fromPath = path.resolve(__dirname, '../CHANGELOG.md')
const toPath = path.resolve(__dirname, '../src/uni_modules/fant-mini-plus')
const docPath = path.resolve(__dirname, '../fant-doc/docs/components')
try {
const file = fs.readFileSync(fromPath, 'utf-8')
fs.writeFileSync(`${toPath}/changelog.md`, file)
fs.writeFileSync(`${docPath}/changelog.md`, file)
} catch (error) {
console.log('CHANGELOG 获取失败')
}

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAqtvdTeEPKGwStj1fVg6UWlMN5uxMv7TQDVs/6vlHSf307ObC
6acjvH+Id2ci5OepN6NgKoGh3P3xwLAXQsOQsCvfnlwiRFaTZ/kD6BWTPSxLHmpf
oS4LYEqUxTrRKnypOarGvqOIvkLNyzpw5En8Vnf1/K67eoFY0UFF87fn4763zlUW
wOuzvVYu56i7FLdU2v0m3UdguJ6pZ3VGpTVfOpkA/nnTjRvPVjFK0BgQBLnHzXUx
1kcA3ZRuCOEFGnyxgaIGwxmD2k5rcwFmvgXkOTaZeERdlRj3As8N7scGsSaIm2RJ
y43lQg2l4gEY2F9HZniGC3H5Qiqa8YttVFGX2wIDAQABAoIBAFUk2eTceeRH7w84
CFFnVJCqgOwJ57lFDsUJKxIahWcfEjYYTRuI+isOVuBB2ka+FzqtxNeJ4DKzrgy6
8+yGbo0MYBSXj1AE4NJYapT2Y3iBoTGYCu3Ud0DWCcs7o06L7vzY2M/ZyOQfgFR9
XBK3t/MTNtdj7/N9j9g/se9hP0LjUEqBBzsE27CGNIu2YzbXxQ5Se72WkM665v5K
Ivkb7Pgh+duN0Bt4hR2BsMUWK/SGOoGoXpM1TgDwzNyRQEgjPOpX/Ipt0AO3jVhL
xS3mFQD6NQckm6l8QTma8su7lFod+K3nvC24QTwbtAfxWdrioLfDyxcGZzUk82Gb
XYXnkIECgYEA1tGQFk1H4bU+ARDrwolPQJI/WPdLs9sscheEeiu2nM6kdbAmwnzm
sY2NtOynksrLMXeEyGCnXtKm7qEH+tUBVB/lsgEXw9Te1gHGZSBk/aPNcA5jXTOA
8684zZBJ6JZVsGmAVbAjti6yUsMhSZk8MjGJnhNKlPqWoURl1q3TmcECgYEAy5zu
wIkd7pmoWHczv9aoMRUUu8xtTNUTq1pfbCb6sc2v4Nocw3+Wtbf3xwe7gwd7QhBn
clwMF/reNslFxyuQQRB1UIOP3igJ6r4sTCPAl8Qsj8xdmN9DMa1WVSLPjCEdj+6Z
0tAnOiYPYCqxG+Hsvbmm5vGk3Kj+nW97lZ8TgJsCgYB95rC2AXEhneHLKimjCGrE
g3JRKA7cSJZR/+qK19fdK1dECouM7Tsf0MC+yvyjketpAI14Cv3NG1TvAr30iqaO
sWsj2nQdOEOp1bx7RHMsHLao+CXQWAE50PZPtEM85+8sx4iJsAQeIFwvGWIHCqI3
IMVxOgk6K2vg9H9jRNmBgQKBgGTpzYbNLnGP5FicE6DToZ5Z5WHCSrWWsV3ut3Zh
x0QSPkYBs9nMxYQgvoP9OBkTvyoZ+Ts7lZ7Y7gNXM+cnlyI4JvyVh9tCGtAmGsxN
t+lACBno3saieKoJT814KEc6Lm2kgsZx9c8jB+HQpuC701qgxbCWOPBILZEXrLeW
FNNbAoGAfxFWLGPXl6oB8qAXX/41v8RhhsziBAuOfoRy7CzkGm1L2BWjTXF/gR/0
xVrNTm/sRIX82zLt/AwWZZja9TutEa3PIGo+E6VaTDIMMyT/vPFol1SWabcRaQgk
KivcysDoP2DLtwYjVznD3WLmxNVN0boNjnZWto3hYzxAqlkVcg4=
-----END RSA PRIVATE KEY-----

46
build/compiler.js Normal file
View File

@ -0,0 +1,46 @@
/*
* @Author: weisheng
* @Date: 2023-03-21 20:58:19
* @LastEditTime: 2023-03-21 20:58:31
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus-plus\build\compiler.js
* 记得注释
*/
const fs = require('fs')
const path = require('path')
const src = path.resolve(__dirname, '../src/uni_modules/fant-mini-plus')
const libDir = path.resolve(__dirname, '../lib')
const copyFile = function (srcPath, tarPath, filter = []) {
fs.mkdir(tarPath, (err) => {})
fs.readdir(srcPath, function (err, files) {
if (err === null) {
files.forEach(function (filename) {
const filedir = path.join(srcPath, filename)
const filterFlag = filter.some((item) => {
return path.extname(filename).toLowerCase() === item && filename !== 'changelog.md'
})
if (!filterFlag) {
fs.stat(filedir, function (errs, stats) {
const isFile = stats.isFile()
if (isFile) {
// 复制文件
const destPath = path.join(tarPath, filename)
fs.copyFile(filedir, destPath, (err) => {})
} else {
// 创建文件夹
const tarFiledir = path.join(tarPath, filename)
copyFile(filedir, tarFiledir, filter) // 递归
}
})
}
})
} else {
if (err) console.error(err)
}
})
}
copyFile(src, libDir, ['.md'])

72
build/deploy.js Normal file
View File

@ -0,0 +1,72 @@
/*
* @Author: weisheng
* @Date: 2022-01-28 14:23:02
* @LastEditTime: 2023-03-21 20:59:16
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\build\deploy.js
* 记得注释
*/
const OSS = require('ali-oss')
const fs = require('fs')
const client = new OSS({
region: 'oss-cn-hongkong',
accessKeyId: 'LTAI5tJ6okg3xgdy4VfCjmzs',
accessKeySecret: '8Hk0Af1CQufErdjrnTI2o5BQmbhY41',
bucket: 'historysoa'
})
async function putOss(ossPath, filePath) {
try {
const result = await client.multipartUpload(ossPath, filePath)
console.log(`上传远程oss文件:${filePath}成功!`)
} catch (e) {
console.log(`上传异常:${e}`)
}
}
/**
* 上传前删除所有的文件
*/
async function deleteAll(object) {
const result = await client.list({
prefix: `${object}`
})
result.objects.forEach((item) => {
client.delete(item.name)
console.log(`删除远程oss文件:${item.name}成功!`)
})
}
/**
* 获取指定文件夹下的文件
* @param {string} local 文件夹路径
*/
async function addFile(local, objectName, srcName) {
const localFiles = fs.readdirSync(local)
localFiles.forEach(async (localFile) => {
// 拼接文件夹子项的路径
const filePath = `${local}/${localFile}`
// 获取子项文件信息
const stat = fs.statSync(filePath)
if (stat.isFile()) {
console.log(srcName, 'srcName')
const ossPath = filePath.split(`${srcName ? srcName : local}`).join(`${objectName}`)
// 上传到oss
await putOss(ossPath, filePath)
} else {
addFile(filePath, objectName, srcName ? srcName : local)
}
})
}
async function upload() {
await deleteAll('fant-mini-plus')
await addFile('fant-doc/dist', 'fant-mini-plus')
await deleteAll('fant-demo')
await addFile('dist/build/h5', 'fant-demo')
}
upload()

54
build/docs.js Normal file
View File

@ -0,0 +1,54 @@
/*
* @Author: weisheng
* @Date: 2022-02-11 15:19:37
* @LastEditTime: 2023-03-28 16:40:46
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\build\docs.js
* 记得注释
*/
const fs = require('fs')
const path = require('path')
/**
* 获取指定文件夹下的文件
* @param {string} local 文件夹路径
*/
function getFile(local) {
const localFiles = fs.readdirSync(local).filter((file) => {
return path.extname(file).toLowerCase() === '' || path.extname(file).toLowerCase() === '.md'
})
const docfile = {
introduction: '', // 简介
instructions: '', // 使用说明
api: ''
} // 文档文件内容
localFiles.forEach((localFile) => {
// 拼接文件夹子项的路径
const filePath = `${local}/${localFile}`
// 获取子项文件信息
const stat = fs.statSync(filePath)
if (stat.isFile()) {
if (localFile === 'API.md') {
// 读取markdown
const file = fs.readFileSync(filePath, 'utf-8')
docfile.api = file.replace(new RegExp(file.split('\n\n', 2)[0], 'g'), '').replace(new RegExp(file.split('\n\n', 2)[1], 'g'), '')
// fs.writeFileSync(`docs/components/${filePath.split('/').reverse()[1]}.md`, file)
} else if (localFile === 'README.md') {
// 读取markdown为数组
docfile.introduction = fs.readFileSync(filePath, 'utf-8')
} else if (localFile === 'INDEX.md') {
// 读取markdown为数组
docfile.instructions = fs.readFileSync(filePath, 'utf-8')
}
} else {
getFile(filePath)
}
})
if (docfile.api || docfile.instructions || docfile.introduction) {
fs.writeFileSync(`fant-doc/docs/components/${local.split('/').reverse()[0]}.md`, docfile.introduction + docfile.instructions + docfile.api)
}
}
// 合并文档
getFile('src/uni_modules/fant-mini-plus/components')

283
build/generate.js Normal file
View File

@ -0,0 +1,283 @@
/*
* @Author: 庞昭昭
* @Date: 2022-02-21 10:23:46
* @LastEditTime: 2023-03-21 20:59:58
* @LastEditors: weisheng
* @Description: 创建文件夹并初始化
* @FilePath: \fant-mini-plus\build\generate.js
* 记得注释
*/
const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const { execSync } = require('child_process')
inquirer
.prompt([
{
type: 'list',
name: 'operation',
message: '请选择操作类型(默认值:✨ create',
choices: ['✨ create 创建', '🐛 modify 编辑', '🚀 remove 移除'],
default: '✨ create 创建'
},
{
type: 'list',
name: 'type',
message: '请选择组件类型(默认值:✨ basic 基础组件)',
choices: ['✨ basic 基础组件', '🐛 form 表单组件', '🚀 action 反馈组件', '🔬 display 展示组件', '🧭 navigation 导航组件'],
default: '✨ basic 基础组件'
},
{
type: 'input',
name: 'oldname',
message: '请输入原组件名',
default: '',
when: function (answers) {
// 当操作不是创建
return answers['operation'] !== '✨ create 创建'
},
validate: function (val) {
if (!val || !val.trim()) {
return '请输入原组件名'
} else {
return true
}
}
},
{
type: 'input',
name: 'name',
message: '请输入组件名',
default: '',
validate: function (val) {
if (!val || !val.trim()) {
return '请输入组件名'
} else {
return true
}
}
},
{
type: 'list',
name: 'confirm',
message: '确认操作吗?',
choices: ['Y', 'N'],
default: 'Y'
}
])
.then((answers) => {
if (!answers['confirm'] || answers['confirm'].toLowerCase() != 'y') {
console.log('🚨 操作取消')
return
}
let name = ''
if (answers['name']) {
name = answers['name']
}
let oldname = ''
if (answers['oldname']) {
oldname = answers['oldname']
}
let type = ''
if (answers['type']) {
type = answers['type'].split(' ')[1]
}
// 文件夹父目录
const parentPath = 'src/uni_modules/fant-mini-plus/components'
// 文件夹目录
const folderPath = `${parentPath}/${name}`
// 操作
switch (answers['operation']) {
case '✨ create 创建':
create(folderPath, type, name) // 新建
break
case '🐛 modify 编辑':
modify(folderPath, type, name, oldname) // 编辑名称
break
case '🚀 remove 移除':
remove(folderPath, type, name, oldname) // 删除
break
default:
break
}
})
.catch((error) => {
if (error.isTtyError) {
// Prompt couldn't be rendered in the current environment
} else {
// Something else went wrong
}
})
// 创建
function create(url, type, name) {
// 检查创建路径是否存在
if (!fs.existsSync(url)) {
// 不存在,创建文件夹
fs.mkdirSync(url)
// vue模板代码
const vueTemplate = `<template>
<view class="${name}"></view>
</template>
<script>
export default {
name: '${name}',
props: {}
}
</script>
<style lang="scss" scoped>
.${name} {}
</style>`
// 创建vue组件
fs.writeFile(`${url}/${name}.vue`, vueTemplate, (err) => {
if (err) throw err
})
// 创建代码演示文档
fs.writeFile(`${url}/INDEX.md`, '## 代码演示', (err) => {
if (err) throw err
})
// 创建组件说明文档
fs.writeFile(`${url}/README.md`, '', (err) => {
if (err) throw err
})
// 更新doc文档
updateDoc('create', type, name)
} else {
console.error('warning文件夹已存在', url)
}
}
// 编辑
function modify(url, type, name, oldname) {
const oldName = oldname
const newName = name
// 判断给定的路径是否存在
if (fs.existsSync(url)) {
if (!newName) {
console.log('error请传入新名称')
return
}
/**
* 返回文件和子目录的数组
*/
files = fs.readdirSync(url)
files.forEach((file, index) => {
// 规范化生成文件路径。
const curPath = path.join(url, file)
/**
* fs.statSync同步读取文件夹文件如果是文件夹在重复触发函数
*/
if (fs.statSync(curPath).isDirectory()) {
// recurse
modify(curPath.replace(/\\/g, '/'))
} else {
// 获取文件内容
const cur = fs.readFileSync(curPath, 'utf-8')
// 替换文件名称
fs.writeFileSync(curPath, cur.replace(new RegExp(oldName, 'g'), newName), (err) => {
if (err) throw err
})
if (file.includes(oldName)) {
// 修改文件名称
fs.renameSync(`${url}/${file}`, `${url}/${file.replace(new RegExp(oldName, 'g'), newName)}`)
}
}
})
/**
* 修改文件夹名称
*/
const newUrlArr = url.split('/')
newUrlArr.splice(newUrlArr.length - 1, 1, newName)
const newUrl = newUrlArr.join('/')
fs.renameSync(url, newUrl, (err) => {
if (err) throw err
})
// 更新doc文档配置
updateDoc(type, name)
} else {
console.error('error给定的路径不存在请给出正确的路径', folderPath)
}
}
// 删除
function remove(url, type, name, oldname) {
// 判断给定的路径是否存在
if (fs.existsSync(url)) {
/**
* 返回文件和子目录的数组
*/
files = fs.readdirSync(url)
files.forEach((file, index) => {
// 规范化生成文件路径。
const curPath = path.join(url, file)
/**
* fs.statSync同步读取文件夹文件如果是文件夹在重复触发函数
*/
if (fs.statSync(curPath).isDirectory()) {
// recurse
remove(curPath)
} else {
// 函数删除文件
fs.unlinkSync(curPath)
}
})
/**
* 清除文件夹
*/
fs.rmdirSync(url)
// 更新doc文档配置
updateDoc(type, name, oldname)
} else {
console.error('error给定的路径不存在请给出正确的路径', folderPath)
}
}
// 更新doc文档新config
function updateDoc(operation, type, name, oldname) {
// 更新config配置
// 获取组件list集合
const cmpList = require(`../fant-doc/docs/.vuepress/cmp/${type}.js`)
// 操作
if (operation == 'create') {
// 将新建文档插入到数组末尾
if (!cmpList.children.includes(`/components/${name}`)) {
// 检查是否已存在文档路径配置,避免重复加入
cmpList.children.push(`/components/${name}`)
// 重写cmpList文件
fs.writeFileSync(`fant-doc/docs/.vuepress/cmp/${type}.js`, `module.exports = ${JSON.stringify(cmpList)}`, (err) => {
if (err) throw err
})
}
} else if (operation == 'modify') {
const oldName = oldname
const newName = name
// 替换文档路径名称
cmpList.children.splice(cmpList.children.indexOf(`/components/${oldName}`), 1, `/components/${newName}`)
// 重写cmpList文件
fs.writeFileSync(`fant-doc/docs/.vuepress/cmp/${type}.js`, `module.exports = ${JSON.stringify(cmpList)}`, (err) => {
if (err) throw err
})
} else if (operation == 'remove') {
// 获取删除文档路径配置下标
const index = cmpList.children.indexOf(`/components/${name}`)
if (index > -1) {
// 检查是否已存在文档路径配置,存在时删除
cmpList.children.splice(index, 1)
// 重写cmpList文件
fs.writeFileSync(`fant-doc/docs/.vuepress/cmp/${type}.js`, `module.exports = ${JSON.stringify(cmpList)}`, (err) => {
if (err) throw err
})
}
} else {
console.log('无操作,未更新文档')
}
}

87
build/release.js Normal file
View File

@ -0,0 +1,87 @@
/*
* @Author: weisheng
* @Date: 2022-11-01 17:12:57
* @LastEditTime: 2023-03-28 16:40:56
* @LastEditors: weisheng
* @Description: 组件发版问答
* @FilePath: \fant-mini-plus\build\release.js
* 记得注释
*/
const inquirer = require('inquirer')
// Node 核心模块
const { execSync } = require('child_process')
const { writeFileSync, readFileSync } = require('fs')
const path = require('path')
const src = path.resolve(__dirname, '../src/uni_modules/fant-mini-plus')
const oldVersion = require('../package.json').version
inquirer
.prompt([
{
type: 'list',
name: 'version',
message: '请选择发版类型(默认值:✨ minor',
choices: ['🐛 patch 小版本', '✨ minor 中版本', '🚀 major 大版本'],
default: '✨ minor 中版本'
},
{
type: 'list',
name: 'release',
message: '确认发布?',
choices: ['Y', 'N'],
default: 'Y'
}
])
.then((answers) => {
if (!answers['release'] || answers['release'].toLowerCase() != 'y') {
console.log('🚨 操作取消')
return
}
// 项目版本更新
switch (answers['version']) {
case '🐛 patch 小版本':
execSync('yarn release-patch')
break
case '✨ minor 中版本':
execSync('yarn release-minor')
break
case '🚀 major 大版本':
execSync('yarn release-major')
break
default:
execSync('yarn release-minor')
break
}
// 生成日志
execSync('yarn changelog')
// 更新版本
const file = readFileSync(path.resolve(__dirname, '../package.json'))
const packageJson = JSON.parse(file.toString())
const version = packageJson.version
console.log(`√ bumping version in package.json from ${oldVersion} to ${version}`)
const package = require('../src/uni_modules/fant-mini-plus/package.json')
package.version = version
writeFileSync(path.resolve(src, 'package.json'), JSON.stringify(package))
// 生成声明文件
execSync('yarn build:types')
console.log('√ build:types complete')
// 生成制品
execSync('yarn compiler')
console.log('√ compiler complete')
execSync(`node build/updateDownloadVersion.js ${oldVersion} ${version}`)
execSync('yarn lint')
execSync('git add -A ')
execSync(`git commit -am "build: compile ${version}"`)
execSync(`git tag -a v${version} -am "chore(release): ${version}"`)
console.log('√ committing changes')
const branch = execSync('git branch --show-current').toString().replace(/\*/g, '').replace(/ /g, '')
console.log('🎉 版本发布成功')
const tip = 'Run `git push --follow-tags origin ' + branch + '` ' + 'to publish'
console.log(tip.replace(/\n/g, ''))
})
.catch((error) => {
if (error.isTtyError) {
// Prompt couldn't be rendered in the current environment
} else {
// Something else went wrong
}
})

44
build/test.js Normal file
View File

@ -0,0 +1,44 @@
/*
* @Author: weisheng
* @Date: 2023-06-10 23:33:04
* @LastEditTime: 2023-06-10 23:42:45
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\build\test.js
* 记得注释
*/
const fs = require('fs')
const path = require('path')
// 文件夹父目录
const src = path.resolve(__dirname, '../src/uni_modules/wot-design-uni/components')
const make = (local) => {
fs.readdir(local, function (err, files) {
if (err === null) {
files.forEach(function (filename) {
const url = path.resolve(local, filename + '/' + filename + '.vue')
// 检查创建路径是否存在
if (!fs.existsSync(url)) {
// vue模板代码
const vueTemplate = `<template>
</template>
<script>
</script>
<style lang="scss" scoped>
</style>`
// 创建vue组件
fs.writeFile(`${url}`, vueTemplate, (err) => {
if (err) throw err
})
} else {
console.error('warning文件夹已存在', url)
}
})
} else {
if (err) console.error(err)
}
})
}
make(src)

View File

@ -0,0 +1,27 @@
/*
* @Author: weisheng
* @Date: 2023-03-14 17:35:30
* @LastEditTime: 2023-03-28 16:41:20
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\build\updateDownloadVersion.js
* 记得注释
*/
const fs = require('fs')
const path = require('path')
const docPath = path.resolve(__dirname, '../fant-doc/docs/components')
// 传入参数
const args = process.argv.splice(2)
const oldVersion = args[0]
const newVersion = args[1]
console.log(oldVersion, 'oldVersion')
console.log(newVersion, 'newVersion')
if (oldVersion && newVersion) {
let installation = fs.readFileSync(`${docPath}/installation.md`, 'utf-8')
installation = installation.replace(new RegExp(`<span >fant-mini-plus@${oldVersion}</span>`, 'g'), `<span >fant-mini-plus@${newVersion}</span>`)
installation = installation.replace(new RegExp('<span >fant-mini-plus</span>', 'g'), `<span >fant-mini-plus@${newVersion}</span>`)
fs.writeFileSync(`${docPath}/installation.md`, installation)
} else {
console.log('组件库压缩包本本更新失败...')
}

12
commitlint.config.js Normal file
View File

@ -0,0 +1,12 @@
/*
* @Author: weisheng
* @Date: 2021-11-24 13:10:52
* @LastEditTime: 2023-03-25 18:12:52
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\commitlint.config.js
* 记得注释
*/
module.exports = {
extends: ['@commitlint/config-conventional']
}

30
index.html Normal file
View File

@ -0,0 +1,30 @@
<!--
* @Author: weisheng
* @Date: 2023-03-21 22:49:24
* @LastEditTime: 2023-04-26 12:43:27
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\index.html
* 记得注释
-->
<!DOCTYPE html>
<html>
<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>
<script src="https://fant-mini-plus.top/touch-emulator.js"></script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

145
package.json Normal file
View File

@ -0,0 +1,145 @@
{
"name": "wot-design-uni",
"version": "0.0.1",
"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": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"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: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": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"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: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",
"type-check": "vue-tsc --noEmit",
"prepare": "husky install",
"lint": "eslint --fix --ext .js,.vue,.ts src",
"commit": "git-cz",
"release-major": "standard-version --release-as major",
"release-minor": "standard-version --release-as minor",
"release-patch": "standard-version --release-as patch",
"clean:lib": "rimraf lib",
"release-lib": "node build/release.js",
"build:types": "tsc -b ./tsconfig.types.json && node ./typesCopy.js",
"compiler": "npm run clean:lib && node build/compiler.js",
"publish-lib": "cd lib && npm publish",
"install:fant-doc": "cd fant-doc && yarn",
"docs:generate": "yarn compiler && node build/docs.js",
"docs:dev": "yarn docs:generate && cd fant-doc && yarn serve",
"docs:build": "yarn docs:generate && cd fant-doc && yarn build",
"deploy": "yarn docs:build && node build/deploy.js",
"component:generate": "node build/generate.js",
"changelog": "node build/changelog.js"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-app-plus": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-components": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-h5": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-alipay": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-baidu": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-jd": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-lark": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-qq": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3080220230511001",
"vue": "^3.2.45",
"vue-i18n": "^9.1.9"
},
"devDependencies": {
"@commitlint/cli": "^17.4.4",
"@commitlint/config-conventional": "^17.4.4",
"@dcloudio/types": "^3.3.2",
"@dcloudio/uni-automator": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3080220230511001",
"@dcloudio/uni-stacktracey": "3.0.0-alpha-3080220230511001",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-3080220230511001",
"@types/node": "^18.14.6",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"@vant/area-data": "^1.4.1",
"@vue/tsconfig": "^0.1.3",
"eslint": "^8.36.0",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.9.0",
"git-cz": "^4.9.0",
"husky": "^8.0.3",
"inquirer": "8.0.0",
"lint-staged": "^13.2.0",
"mini-types": "^0.1.7",
"miniprogram-api-typings": "^3.9.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.4",
"query-string": "^8.1.0",
"rimraf": "^4.4.0",
"rollup-plugin-visualizer": "^5.9.0",
"sass": "^1.59.3",
"standard-version": "^9.5.0",
"typescript": "^4.9.4",
"uni-mini-router": "^0.0.12",
"uni-read-pages-vite": "^0.0.6",
"vite": "4.0.3",
"vitest": "^0.30.1",
"vue-eslint-parser": "^9.1.0",
"vue-tsc": "^1.0.24"
},
"config": {
"commitizen": {
"path": "git-cz"
}
},
"standard-version": {
"skip": {
"tag": true
}
},
"browserslist": [
"Android >= 4.4",
"ios >= 9"
],
"lint-staged": {
"*.{js,ts,vue}": "eslint --fix --ext .js,.vue,.ts src"
},
"uni-app": {
"scripts": {
"mp-dingtalk": {
"title": "钉钉小程序",
"env": {
"UNI_PLATFORM": "mp-alipay"
},
"define": {
"MP-DINGTALK": true
}
}
}
},
"files": [
"lib"
]
}

22
src/App.vue Normal file
View File

@ -0,0 +1,22 @@
<!--
* @Author: weisheng
* @Date: 2023-03-09 19:23:03
* @LastEditTime: 2023-06-11 13:23:52
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\App.vue
* 记得注释
-->
<script setup lang="ts">
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
onLaunch((ctx) => {
console.log('App Launch')
})
onShow(() => {
console.log('App Show')
})
onHide(() => {
console.log('App Hide')
})
</script>
<style lang="scss"></style>

View File

@ -0,0 +1,52 @@
<template>
<view :class="['demo-block', transparent ? '' : 'is-white', 'custom-class']">
<view class="demo-title">{{ title }}</view>
<view class="demo-container" :style="transparent ? '' : style">
<slot />
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
interface Props {
title: string
ver: number
hor: number
transparent: boolean
}
const props = withDefaults(defineProps<Props>(), {
ver: 10,
hor: 15
})
const style = ref<string>('')
watch(
[() => props.ver, () => props.hor],
() => {
setStyle()
},
{ deep: true, immediate: true }
)
function setStyle() {
style.value = `margin: ${props.ver}px ${props.hor}px`
}
</script>
<style lang="scss" scoped>
.demo-block {
margin: 15px 0;
color: #666;
overflow: hidden;
}
.is-white {
background: #fff;
}
.demo-title {
padding: 0 15px;
margin: 10px 0;
font-size: 13px;
}
</style>

17
src/env.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
/*
* @Author: weisheng
* @Date: 2023-03-21 21:06:55
* @LastEditTime: 2023-03-21 21:07:02
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\src\env.d.ts
*
*/
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}

20
src/main.ts Normal file
View File

@ -0,0 +1,20 @@
/*
* @Author: weisheng
* @Date: 2023-03-09 19:23:03
* @LastEditTime: 2023-06-10 23:01:56
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\main.ts
*
*/
import { createSSRApp } from 'vue'
import App from './App.vue'
import router from './router'
export function createApp() {
const app = createSSRApp(App)
app.config.warnHandler = () => null
app.use(router)
return {
app
}
}

72
src/manifest.json Normal file
View File

@ -0,0 +1,72 @@
{
"name" : "",
"appid" : "",
"description" : "",
"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" : "wxa3fab3dcde4667f0",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics": {
"enable": false
},
"vueVersion" : "3"
}

13
src/model/KV.ts Normal file
View File

@ -0,0 +1,13 @@
/*
* @Author: weisheng
* @Date: 2021-12-28 15:57:08
* @LastEditTime: 2021-12-28 15:58:09
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini\src\model\KV.ts
*
*/
export default class KV<T> {
label: string = '' // 操作标题
value!: T // 操作项数组
}

View File

@ -0,0 +1,16 @@
import KV from './KV'
/*
* @Author: weisheng
* @Date: 2021-12-28 15:13:43
* @LastEditTime: 2023-03-21 22:34:12
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\src\model\OperationOption.ts
*
*/
export default class OperationOption {
label: string = '' // 操作的标题(用于展示)
name: string | string[] = '' // 操作真实值用于代码传参
value: KV<any>[] = [] // 操作项数组
}

47
src/pages.json Normal file
View File

@ -0,0 +1,47 @@
{
"pages": [
{
"path": "pages/button/Button",
"name": "button",
"style": {
"mp-alipay": {
"allowsBounceVertical": "NO"
},
"navigationBarTitleText": "Button 按钮"
}
},
{
"path": "pages/icon/Icon",
"name": "icon",
"style": {
"mp-alipay": {
"allowsBounceVertical": "NO"
},
"navigationBarTitleText": "Icon 图标"
}
},
{
"path": "pages/badge/Badge",
"name": "badge",
"style": {
"mp-alipay": {
"allowsBounceVertical": "NO"
},
"navigationBarTitleText": "Badge 徽标"
}
}
],
"tabBar": {
"color": "#7a7e83",
"selectedColor": "#1C64FD",
"backgroundColor": "#ffffff",
"list": [ ]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#FFF",
"backgroundColor": "#F8F8F8"
}
}

54
src/pages/badge/Badge.vue Normal file
View File

@ -0,0 +1,54 @@
<template>
<demo-block title="展示消息数量">
<wd-badge custom-class="badge" :modelValue="12">
<wd-button :round="false" type="info" size="small">评论</wd-button>
</wd-badge>
<wd-badge custom-class="badge" :modelValue="3" bg-color="pink">
<wd-button :round="false" type="info" size="small">回复</wd-button>
</wd-badge>
<wd-badge custom-class="badge" :modelValue="1" type="primary">
<wd-button :round="false" type="info" size="small">评论</wd-button>
</wd-badge>
<wd-badge custom-class="badge" :modelValue="2" type="warning">
<wd-button :round="false" type="info" size="small">回复</wd-button>
</wd-badge>
<wd-badge custom-class="badge" :modelValue="1" type="success">
<wd-button :round="false" type="info" size="small">评论</wd-button>
</wd-badge>
<wd-badge custom-class="badge" :modelValue="2" type="info">
<wd-button :round="false" type="info" size="small">回复</wd-button>
</wd-badge>
</demo-block>
<demo-block title="可定义消息最大值">
<wd-badge custom-class="badge" :modelValue="200" max="99">
<wd-button :round="false" type="info" size="small">评论</wd-button>
</wd-badge>
<wd-badge custom-class="badge" :modelValue="200" max="10">
<wd-button :round="false" type="info" size="small">回复</wd-button>
</wd-badge>
</demo-block>
<demo-block title="自定义内容">
<wd-badge custom-class="badge" model:modelValue="new">
<wd-button :round="false" type="info" size="small">评论</wd-button>
</wd-badge>
<wd-badge custom-class="badge" model:modelValue="hot">
<wd-button :round="false" type="info" size="small">回复</wd-button>
</wd-badge>
</demo-block>
<demo-block title="点状类型">
<wd-badge custom-class="badge" is-dot>数据查询</wd-badge>
<wd-badge custom-class="badge" is-dot>
<wd-button :round="false" type="info" size="small">回复</wd-button>
</wd-badge>
</demo-block>
</template>
<script setup lang="ts"></script>
<style lang="scss" scoped>
.badge {
margin: 0 30px 20px 0;
display: inline-block;
}
</style>

1
src/pages/badge/index.js Normal file
View File

@ -0,0 +1 @@
Page({})

View File

@ -0,0 +1,8 @@
{
"navigationBarTitleText": "Badge 角标",
"usingComponents": {
"demo-block": "../../components/demo-block/index",
"wd-badge": "../../wot-design/badge/index",
"wd-button": "../../wot-design/button/index"
}
}

132
src/pages/badge/index.jxml Normal file
View File

@ -0,0 +1,132 @@
<demo-block title="展示消息数量">
<wd-badge
custom-class="badge"
value="12"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>评论</wd-button>
</wd-badge>
<wd-badge
custom-class="badge"
value="3"
bg-color="pink"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>回复</wd-button>
</wd-badge>
<wd-badge
custom-class="badge"
value="1"
type="primary"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>评论</wd-button>
</wd-badge>
<wd-badge
custom-class="badge"
value="2"
type="warning"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>回复</wd-button>
</wd-badge>
<wd-badge
custom-class="badge"
value="1"
type="success"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>评论</wd-button>
</wd-badge>
<wd-badge
custom-class="badge"
value="2"
type="info"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>回复</wd-button>
</wd-badge>
</demo-block>
<demo-block title="可定义消息最大值">
<wd-badge
custom-class="badge"
value="200"
max="99"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>评论</wd-button>
</wd-badge>
<wd-badge
custom-class="badge"
value="200"
max="10"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>回复</wd-button>
</wd-badge>
</demo-block>
<demo-block title="自定义内容">
<wd-badge
custom-class="badge"
value="new"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>评论</wd-button>
</wd-badge>
<wd-badge
custom-class="badge"
value="hot"
>
<wd-button
round="{{false}}"
type="info"
size="small"
>回复</wd-button>
</wd-badge>
</demo-block>
<demo-block title="点状类型">
<wd-badge
custom-class="badge"
is-dot
>数据查询</wd-badge>
<wd-badge
custom-class="badge"
is-dot
>
<wd-button
round="{{false}}"
type="info"
size="small"
>回复</wd-button>
</wd-badge>
</demo-block>

View File

@ -0,0 +1,4 @@
.badge {
margin: 0 30px 20px 0;
display: inline-block;
}

118
src/pages/button/Button.vue Normal file
View File

@ -0,0 +1,118 @@
<template>
<view>
<demo-block title="基本用法">
<wd-button open-type="getUserInfo" @getuserinfo="handleGetuserinfo">主要按钮</wd-button>
<wd-button type="success">成功按钮</wd-button>
<wd-button type="info">信息按钮</wd-button>
<wd-button type="warning">警告按钮</wd-button>
<wd-button type="error">危险按钮</wd-button>
</demo-block>
<demo-block title="禁用按钮">
<wd-button disabled>主要按钮</wd-button>
<wd-button type="success" disabled>成功按钮</wd-button>
<wd-button type="info" disabled>信息按钮</wd-button>
<wd-button type="warning" disabled>警告按钮</wd-button>
<wd-button type="error" disabled>危险按钮</wd-button>
</demo-block>
<demo-block title="幽灵按钮">
<wd-button plain>主要按钮</wd-button>
<wd-button type="success" plain>成功按钮</wd-button>
<wd-button type="info" plain>信息按钮</wd-button>
<wd-button type="warning" plain>警告按钮</wd-button>
<wd-button type="error" plain>危险按钮</wd-button>
</demo-block>
<demo-block title="幽灵按钮禁用状态">
<wd-button plain disabled>主要按钮</wd-button>
<wd-button type="success" plain disabled>成功按钮</wd-button>
<wd-button type="info" plain disabled>信息按钮</wd-button>
<wd-button type="warning" plain disabled>警告按钮</wd-button>
<wd-button type="error" plain disabled>危险按钮</wd-button>
</demo-block>
<demo-block title="按钮大小">
<wd-button size="small">小型按钮</wd-button>
<wd-button size="medium">普通按钮</wd-button>
<wd-button size="large">大型按钮</wd-button>
</demo-block>
<demo-block title="加载中">
<wd-button loading>加载中</wd-button>
<wd-button type="success" loading>加载中</wd-button>
<wd-button type="warning" loading>加载中</wd-button>
<wd-button type="error" loading>加载中</wd-button>
<wd-button type="info" loading>加载中</wd-button>
</demo-block>
<demo-block title="文字按钮">
<wd-button type="text">按钮</wd-button>
<wd-button type="text" disabled>按钮</wd-button>
</demo-block>
<demo-block title="图标按钮">
<wd-button type="icon" icon="delete-thin"></wd-button>
<wd-button type="icon" icon="delete-thin" disabled></wd-button>
</demo-block>
<demo-block title="带图标的基本按钮">
<wd-button icon="download">下载</wd-button>
<wd-button icon="setting">设置</wd-button>
</demo-block>
<demo-block title="块状按钮宽度100%">
<wd-button block size="large">主要按钮</wd-button>
<wd-button type="success" block size="large">成功按钮</wd-button>
<wd-button type="info" block size="large">信息按钮</wd-button>
<wd-button type="warning" block size="large">警告按钮</wd-button>
<wd-button type="error" block size="large">危险按钮</wd-button>
</demo-block>
<demo-block title="常用按钮:块状+圆角">
<wd-button block size="large" disabled>主要按钮</wd-button>
<wd-button block size="large">主要按钮</wd-button>
<wd-button block size="large" loading>主要按钮</wd-button>
<wd-button type="info" block size="large" disabled>信息按钮</wd-button>
<wd-button type="info" block size="large">信息按钮</wd-button>
</demo-block>
<demo-block title="常用按钮:圆角或圆角+幽灵">
<view>
<wd-button disabled>主操作</wd-button>
<wd-button size="small" disabled>主操作</wd-button>
</view>
<view>
<wd-button>主操作</wd-button>
<wd-button size="small">主操作</wd-button>
</view>
<view>
<wd-button type="info" disabled>次操作</wd-button>
<wd-button type="info" size="small" disabled>次操作</wd-button>
</view>
<view>
<wd-button type="info">次操作</wd-button>
<wd-button type="info" size="small">次操作</wd-button>
</view>
<view>
<wd-button plain disabled>幽灵按钮</wd-button>
<wd-button size="small" plain disabled>幽灵按钮</wd-button>
</view>
<view>
<wd-button plain>幽灵按钮</wd-button>
<wd-button size="small" plain>幽灵按钮</wd-button>
</view>
<view>
<wd-button type="info" plain disabled>次操作</wd-button>
<wd-button type="info" size="small" plain disabled>次操作</wd-button>
</view>
<view>
<wd-button type="info" plain>次操作</wd-button>
<wd-button type="info" size="small" plain>次操作</wd-button>
</view>
</demo-block>
</view>
</template>
<script lang="ts" setup>
function handleGetuserinfo(event) {
// TODO
console.log(event)
}
</script>
<style lang="scss" scoped>
button {
margin: 0 10px 10px 0;
}
.button-block {
margin-right: 0;
}
</style>

93
src/pages/icon/Icon.vue Normal file
View File

@ -0,0 +1,93 @@
<template>
<view class="icon-list">
<view v-for="(icon, index) in icons" :key="index" class="icon-item">
<view><wd-icon :name="icon" size="22px" /></view>
<view class="icon-item-name">{{ icon }}</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const icons = ref([
'keyboard-collapse',
'keyboard-delete',
'arrow-thin-up',
'arrow-thin-down',
'rotate',
'setting',
'arrow-right',
'arrow-down',
'arrow-up',
'arrow-left',
'fill-camera',
'star-on',
'check-bold',
'error-fill',
'warn-bold',
'close-outline',
'close-bold',
'check-outline',
'wifi-error',
'subscribe',
'detection',
'read',
'fill-arrow-down',
'dong',
'edit-outline',
'add-circle',
'copy',
'bags',
'decrease',
'jdm',
'spool',
'download',
'phone-compute',
'computer',
'clock',
'view',
'eye-close',
'picture',
'evaluation',
'goods',
'lenovo',
'list',
'note',
'video',
'warning',
'camera',
'transfer',
'keywords',
'chat',
'delete-thin',
'add',
'scan',
'translate-bold',
'close',
'search',
'delete',
'more',
'thin-arrow-left',
'filter',
'phone',
'refresh',
'check'
])
</script>
<style lang="scss" scoped>
.icon-list {
display: flex;
margin: 15px;
flex-wrap: wrap;
background: #fff;
}
.icon-item {
width: 25%;
padding: 15px 0;
text-align: center;
}
.icon-item-name {
margin: 10px 0;
color: #999;
}
</style>

27
src/router/index.ts Normal file
View File

@ -0,0 +1,27 @@
/*
* @Author: weisheng
* @Date: 2021-10-13 11:15:00
* @LastEditTime: 2023-04-06 20:10:46
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\src\router\index.ts
*
*/
import { createRouter } from 'uni-mini-router'
const router = createRouter({
routes: [...ROUTES]
})
router.beforeEach((to, from, next) => {
console.log(to, 'to')
console.log(from, 'from')
console.log('进入路由之前调用')
next()
})
router.afterEach((to, from) => {
console.log(to, 'to')
console.log(from, 'from')
console.log('进入路由之后调用')
})
export default router

15
src/shime-uni.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
/* eslint-disable @typescript-eslint/no-empty-interface */
/*
* @Author: weisheng
* @Date: 2023-03-09 19:23:03
* @LastEditTime: 2023-03-21 21:30:30
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\src\shime-uni.d.ts
*
*/
export {}
declare module 'vue' {
type Hooks = App.AppInstance & Page.PageInstance
interface ComponentCustomOptions extends Hooks {}
}

11
src/types.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
/*
* @Author: weisheng
* @Date: 2023-03-09 21:36:22
* @LastEditTime: 2023-03-21 21:32:12
* @LastEditors: weisheng
* @Description:
* @FilePath: \fant-mini-plus\src\types.d.ts
*
*/
//type.d.ts
declare const ROUTES: []

1
src/uni.scss Normal file
View File

@ -0,0 +1 @@

View File

@ -0,0 +1,7 @@
/**
* SCSS 配置项命名空间以及BEM
*/
$namespace: 'wd';
$elementSeparator: '__';
$modifierSeparator: '--';
$state-prefix: 'is-';

View File

@ -0,0 +1,80 @@
/**
* 辅助函数
*/
@import 'config';
$default-theme: #4d80f0 !default; // 正常色
/* 转换成字符串 */
@function selectorToString($selector) {
$selector: inspect($selector);
$selector: str-slice($selector, 2, -2);
@return $selector;
}
/* 判断是否存在 Modifier */
@function containsModifier($selector) {
$selector: selectorToString($selector);
@if str-index($selector, $modifierSeparator) {
@return true;
} @else {
@return false;
}
}
/* 判断是否存在伪类 */
@function containsPseudo($selector) {
$selector: selectorToString($selector);
@if str-index($selector, ':') {
@return true;
} @else {
@return false;
}
}
/**
* 主题色切换
* @params $theme-color 主题色
* @params $type 变暗dark 变亮 'light'
* @params $mix-color 自己设置的混色
*/
@function themeColor($theme-color, $type: "", $mix-color: "") {
@if $default-theme != #4d80f0 {
@if $type == "dark" {
@return darken($theme-color, 10%);
} @else if $type == "light" {
@return lighten($theme-color, 10%);
} @else {
@return $theme-color;
}
} @else {
@return $mix-color;
}
}
/**
* 颜色结果切换 如果开启线性渐变色 使用渐变色如果没有开启那么使用主题色
* @params $open-linear 是否开启线性渐变色
* @params $deg 渐变色角度
* @params $theme-color 当前配色
* @params [Array] $set 主题色明暗设置 $color-list 数量对应
* @params [Array] $color-list 渐变色顺序 $color-list $per-list 数量相同
* @params [Array] $per-list 渐变色比例
*/
@function resultColor($open-linear, $deg, $theme-color, $set, $color-list, $per-list) {
// 开启渐变
@if $open-linear {
$len: length($color-list);
$arg: $deg;
@for $i from 1 through $len {
$arg: $arg + "," + themeColor($theme-color, nth($set, $i), nth($color-list, $i)) + " " + nth($per-list, $i);
}
@return linear-gradient(unquote($arg));
} @else {
// 不开启渐变 直接使用色值
@return $theme-color;
}
}

View File

@ -0,0 +1,265 @@
/**
* 混合宏
*/
@import "config";
@import "function";
/**
* BEM定义块b)
*/
@mixin b($block) {
$B: $namespace + "-" + $block !global;
.#{$B} {
@content;
}
}
/* 定义元素e对于伪类会自动将 e 嵌套在 伪类 底下 */
@mixin e($element...) {
$selector: &;
$selectors: "";
@if containsPseudo($selector) {
@each $item in $element {
$selectors: #{$selectors + "." + $B + $elementSeparator + $item + ","};
}
@at-root {
#{$selector} {
#{$selectors} {
@content;
}
}
}
} @else {
@each $item in $element {
$selectors: #{$selectors + $selector + $elementSeparator + $item + ","};
}
@at-root {
#{$selectors} {
@content;
}
}
}
}
/* 定义状态m */
@mixin m($modifier...) {
$selectors: "";
@each $item in $modifier {
$selectors: #{$selectors + & + $modifierSeparator + $item + ","};
}
@at-root {
#{$selectors} {
@content;
}
}
}
/* 对于需要需要嵌套在 m 底下的 e调用这个混合宏一般在切换整个组件的状态如切换颜色的时候 */
@mixin me($element...) {
$selector: &;
$selectors: "";
@if containsModifier($selector) {
@each $item in $element {
$selectors: #{$selectors + "." + $B + $elementSeparator + $item + ","};
}
@at-root {
#{$selector} {
#{$selectors} {
@content;
}
}
}
} @else {
@each $item in $element {
$selectors: #{$selectors + $selector + $elementSeparator + $item + ","};
}
@at-root {
#{$selectors} {
@content;
}
}
}
}
/* 状态,生成 is-$state 类名 */
@mixin when($state) {
@at-root {
&.#{$state-prefix + $state} {
@content;
}
}
}
/**
* 常用混合宏
*/
/* 单行超出隐藏 */
@mixin lineEllipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 多行超出隐藏 */
@mixin multiEllipsis($lineNumber: 3) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $lineNumber;
overflow: hidden;
}
/* 清除浮动 */
@mixin clearFloat {
&::after {
display: block;
content: "";
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
}
/* 0.5px 边框 */
@mixin halfPixelBorder($direction: "bottom", $left: 0, $color: $-color-border-light) {
&::after {
position: absolute;
display: block;
content: "";
@if ($left == 0) {
width: 100%;
} @else {
width: calc(100% - #{$left});
}
height: 1px;
left: $left;
@if ($direction == "bottom") {
bottom: 0;
} @else {
top: 0;
}
transform: scaleY(0.5);
background: $color;
}
}
@mixin buttonClear {
outline: none;
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent;
background: transparent;
}
/**
* 三角形实现尖角样式适用于背景透明情况
* @param $size 三角形高底边为 $size * 2
* @param $bg 三角形背景颜色
*/
@mixin triangleArrow($size, $bg) {
@include e(arrow) {
position: absolute;
width: 0;
height: 0;
}
@include e(arrow-down) {
border-left: $size solid transparent;
border-right: $size solid transparent;
border-top: $size solid $bg;
transform: translateX(-50%);
bottom: -$size;
}
@include e(arrow-up) {
border-left: $size solid transparent;
border-right: $size solid transparent;
border-bottom: $size solid $bg;
transform: translateX(-50%);
top: -$size;
}
@include e(arrow-left) {
border-top: $size solid transparent;
border-bottom: $size solid transparent;
border-right: $size solid $bg;
transform: translateY(-50%);
left: -$size;
}
@include e(arrow-right) {
border-top: $size solid transparent;
border-bottom: $size solid transparent;
border-left: $size solid $bg;
transform: translateY(-50%);
right: -$size;
}
}
/**
* 正方形实现尖角样式适用于背景不透明情况
* @param $size 正方形边长
* @param $bg 正方形背景颜色
* @param $z-index z-index属性值不得大于外部包裹器
* @param $box-shadow 阴影
*/
@mixin squareArrow($size, $bg, $z-index, $box-shadow) {
@include e(arrow) {
position: absolute;
width: $size;
height: $size;
z-index: $z-index;
}
@include e(arrow-down) {
transform: translateX(-50%);
bottom: 0;
&:after {
content: "";
width: $size;
height: $size;
background-color: $bg;
position: absolute;
bottom: -$size/2;
transform: rotateZ(45deg);
box-shadow: $box-shadow;
}
}
@include e(arrow-up) {
transform: translateX(-50%);
&:after {
content: "";
width: $size;
height: $size;
background-color: $bg;
position: absolute;
top: -$size/2;
transform: rotateZ(45deg);
box-shadow: $box-shadow;
}
}
@include e(arrow-left) {
transform: translateY(-50%);
&:after {
content: "";
width: $size;
height: $size;
background-color: $bg;
position: absolute;
left: -$size/2;
transform: rotateZ(45deg);
box-shadow: $box-shadow;
}
}
@include e(arrow-right) {
transform: translateY(-50%);
&:after {
content: "";
width: $size;
height: $size;
background-color: $bg;
position: absolute;
right: -$size/2;
transform: rotateZ(45deg);
box-shadow: $box-shadow;
}
}
}

View File

@ -0,0 +1,829 @@
@import './function';
$open-linear: true !default;
/**
* UI规范基础变量
*/
/*----------------------------------------- Theme color. start ----------------------------------------*/
/* 主题颜色 */
$-color-theme: $default-theme !default; // 品牌色
$-color-white: #ffffff !default; // 用于mix的白色
$-color-black: #000000 !default; // 用于mix的黑色
/* 辅助色 */
$-color-success: #34d19d !default; // 成功色
$-color-warning: #f0883a !default; // 警告色
$-color-danger: #fa4350 !default; // 危险出错色
$-color-purple: #8268de !default; // 紫色
$-color-yellow: #f0cd1d !default; // 黄色
$-color-blue: #2bb3ed !default; // 蓝色
// TODO 全部替换后如果不用此处就删除
$-color-info: #909399 !default;
$-color-theme-light: mix($-color-white, $-color-theme, 42%) !default; // 弱化色
$-color-theme-emph: mix($-color-black, $-color-theme, 18%) !default; // 强调色
/* 文字颜色(默认浅色背景下 */
$-color-title: $-color-black !default; // 模块标题/重要正文 000
$-color-content: mix($-color-black, $-color-white, 85%) !default; // 普通正文 262626
$-color-secondary: mix($-color-black, $-color-white, 65%) !default; // 次要信息注释/补充/正文 595959
$-color-aid: mix($-color-black, $-color-white, 45%) !default; // 辅助文字字号弱化信息引导性/不可点文字 8c8c8c
$-color-tip: mix($-color-black, $-color-white, 25%) !default; // 失效默认提示文字 bfbfbf
$-color-border: mix($-color-black, $-color-white, 15%) !default; // 控件边框线 d9d9d9
$-color-border-light: mix($-color-black, $-color-white, 9%) !default; // 分割线颜色 e8e8e8
$-color-bg: mix($-color-black, $-color-white, 4%) !default; // 背景色禁用填充色 f5f5f5
$-color-table-bg: mix($-color-black, $-color-white, 2%) !default; // 表头填充 fafafa
/* 文字颜色-深背景 */
$-color-title-indark: $-color-white !default; // 模块标题/重要正文 fff
$-color-text-indark: mix($-color-white, $-color-black, 85%) !default; // 普通正文 d9d9d9
$-color-secondary-indark: mix($-color-white, $-color-black, 65%) !default; // 次要信息注释/补充/正文 a6a6a6
$-color-aid-indark: mix($-color-white, $-color-black, 45%) !default; // 次级按钮边框线 737373
$-color-tip-indark: mix($-color-white, $-color-black, 25%) !default; // 失效默认提示文字 404040
$-color-border-indark: mix($-color-white, $-color-black, 15%) !default; // 控件边框线 262626
$-color-border-light-indark: mix($-color-white, $-color-black, 9%) !default; // 分割线颜色 171717
$-color-bg-indark: mix($-color-white, $-color-black, 4%) !default; // 背景色禁用填充色 0a0a0a
$-color-table-bg-indark: mix($-color-white, $-color-black, 2%) !default; // 表头填充 fafafa
// TODO 待替换
/* 透明度 */
$-color-inlight-title-mask: mix($-color-black, $-color-white, 100%) !default; // 模块标题/重要正文 000
/* 图形颜色 */
$-color-icon: mix($-color-black, $-color-white, 15%) !default; // icon颜色
$-color-icon-active: #eee !default; // icon颜色hover
$-color-icon-disabled: #a7a7a7 !default; // icon颜色disabled
/* modal */
$-modal-bg: rgba($-color-black, 0.65) !default;
/*----------------------------------------- Theme color. end -------------------------------------------*/
/*-------------------------------- Theme color application size. start --------------------------------*/
/* 文字字号 */
$-fs-big: 24px !default; // 大型标题
$-fs-important: 19px !default; // 重要数据
$-fs-title: 16px !default; // 标题字号/重要正文字号
$-fs-content: 14px !default; // 普通正文
$-fs-secondary: 12px !default; // 次要信息注释/补充/正文
$-fs-aid: 10px !default; // 辅助文字字号弱化信息引导性/不可点文字
/* 文字字重 */
$-fw-medium: 500 !default; // PingFangSC-Medium
$-fw-semibold: 600 !default; // PingFangSC-Semibold
/* 尺寸 */
$-size-side-padding: 15px !default; // 屏幕两边留白
/*-------------------------------- Theme color application size. end --------------------------------*/
/* action-sheet */
$-action-sheet-weight: 500 !default; // 面板字重
$-action-sheet-radius: 16px !default; // 面板圆角大小
$-action-sheet-action-height: 48px !default; // 单条菜单高度
$-action-sheet-color: rgba($-color-black, 0.85) !default; // 选项名称颜色
$-action-sheet-fs: $-fs-title !default; // 选项名称字号
$-action-sheet-active-color: $-color-bg !default; // 点击高亮颜色
$-action-sheet-subname-fs: $-fs-secondary !default; // 描述信息字号
$-action-sheet-subname-color: rgba($-color-black, 0.45) !default; // 描述信息颜色
$-action-sheet-disabled-color: rgba($-color-black, 0.25) !default; // 禁用颜色
$-action-sheet-bg: $-color-white !default; // 菜单容器颜色取消按钮上方的颜色
$-action-sheet-title-height: 64px !default; // 标题高度
$-action-sheet-title-fs: $-fs-title !default; // 标题字号
$-action-sheet-close-fs: $-fs-title !default; // 关闭按钮大小
$-action-sheet-close-color: rgba($-color-black, 0.65) !default; // 关闭按钮颜色
$-action-sheet-close-top: 25px !default; // 关闭按钮距离标题顶部距离
$-action-sheet-close-right: 15px !default; // 关闭按钮距离标题右侧距离
$-action-sheet-cancel-color: #131415 !default; // 取消按钮颜色
$-action-sheet-cancel-height: 44px !default; // 取消按钮高度
$-action-sheet-cancel-bg: rgba(240,240,240,1) !default; // 取消按钮背景色
$-action-sheet-cancel-radius: 22px !default; // 取消按钮圆角大小
$-action-sheet-panel-padding: 12px 0 11px !default; // 自定义面板内边距大小
$-action-sheet-panel-img-fs: 40px !default; // 自定义面板图片大小
$-action-sheet-panel-img-radius: 4px !default; // 自定义面板图片圆角大小
/* badge */
$-badge-bg: $-color-danger !default; // 背景填充颜色
$-badge-color: #fff !default; // 文字颜色
$-badge-fs: 12px !default; // 文字字号
$-badge-padding: 0 5px !default; // padding
$-badge-height: 16px !default; // 高度
$-badge-primary: $-color-theme !default;
$-badge-success: $-color-success !default;
$-badge-warning: $-color-warning !default;
$-badge-danger: $-color-danger !default;
$-badge-info: $-color-info !default;
$-badge-dot-size: 6px !default; // dot 类型大小
$-badge-border: 2px solid $-badge-color; // 边框样式
/* button */
$-button-small-height: 28px !default; // 小型按钮高度
$-button-small-padding: 0 11px !default; // 小型按钮padding
$-button-small-fs: $-fs-secondary !default; // 小型按钮字号
$-button-small-radius: 2px !default; // 小型按钮圆角大小
$-button-small-loading: 14px !default; // 小型按钮loading图标大小
$-button-medium-height: 36px !default; // 中型按钮高度
$-button-medium-padding: 0 15px !default; // 中型按钮padding
$-button-medium-fs: $-fs-content !default; // 中型按钮字号
$-button-medium-radius: 4px !default; // 中型按钮圆角大小
$-button-medium-loading: 18px !default; // 中型按钮loading图标大小
$-button-medium-box-shadow-size: 0px 2px 4px 0px !default; // 中尺寸阴影尺寸
$-button-large-height: 44px !default; // 大型按钮高度
$-button-large-padding: 0 36px !default; // 大型按钮padding
$-button-large-fs: $-fs-title !default; // 大型按钮字号
$-button-large-radius: 8px !default; // 大型按钮圆角大小
$-button-large-loading: 24px !default; // 大小按钮loading图标大小
$-button-large-box-shadow-size: 0px 4px 8px 0px !default; // 大尺寸阴影尺寸
$-button-icon-fs: 18px !default; // 带图标的按钮的图标大小
$-button-icon-size: 40px !default; // icon 类型按钮尺寸
$-button-icon-color: rgba($-color-black, 0.65) !default; // icon 类型按钮颜色
$-button-icon-active-color: $-color-icon-active !default; // icon 类型按钮点击态颜色
$-button-icon-disabled-color: $-color-icon-disabled !default; // icon 类型按钮禁用颜色
$-button-normal-bg: $-color-white !default; // 默认按钮禁用背景色
$-button-normal-color: $-color-title !default; // 文字颜色
$-button-normal-active-color: $-color-black !default; // 文字点击态颜色
$-button-normal-active-bg: $-color-bg !default; // 默认按钮点击态背景色
$-button-normal-border-active-color: rgba($-color-black, 0.45) !default; // 默认按钮点击态边框色
$-button-normal-disabled-color: rgba($-color-black, 0.25) !default; // 默认按钮禁用文字色
$-button-normal-disabled-bg: $-color-white !default; // 默认按钮禁用背景色
$-button-normal-border-disabled-color: $-color-bg !default; // 默认按钮禁用边框色
$-button-border-color: mix($-color-black, $-color-white, 65%) !default; // 默认按钮边框色
$-button-primary-color: $-color-theme !default; // 主要按钮颜色
$-button-primary-bg-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light",
#4f7cf8 #668df8,
0% 100%
) !default; // 主要按钮背景颜色
$-button-primary-active-color: resultColor(
$open-linear,
315deg,
darken($-color-theme, 7%),
"dark" "light",
#416bdf #4e79ee,
0% 100%
) !default; // 主要按钮点击态颜色
$-button-primary-plain-active-bg-color: #eff4fe !default; // 主要按钮点击态颜色
$-button-primary-disabled-color: resultColor(
$open-linear,
315deg,
rgba($-color-theme, 0.6),
"dark" "light",
#8FADFF #9FB8FE,
0% 100%
) !default; // 主要按钮禁用颜色
$-button-primary-plain-disabled-color: themeColor($-color-theme, "light", #9DB9F6) !default; // 主要按钮点击态文字颜色
$-button-primary-box-shadow-color: rgba($-color-theme, 0.25) !default; // 主要按钮阴影颜色
$-button-success-color: $-color-success !default; // 成功按钮颜色
$-button-success-active-color: mix($-color-black, $-button-success-color, 18%) !default; // 成功按钮点击态颜色
$-button-success-disabled-color: mix($-color-white, $-button-success-color, 42%) !default; // 成功按钮禁用颜色
$-button-success-box-shadow-color: rgba($-color-success, 0.25) !default; // 主要按钮阴影颜色
$-button-info-bg-color: #F0F0F0 !default; // 信息按钮背景颜色
$-button-info-color: $-color-title !default; // 信息按钮颜色
$-button-info-active-bg-color: #E1E1E1 !default; // 信息按钮背景颜色
$-button-info-active-color: rgba($-color-black, 0.85) !default; // 信息按钮点击态颜色
$-button-info-disabled-bg-color: #F0F0F0 !default; // 信息按钮禁用颜色
$-button-info-disabled-color: rgba($-color-black, 0.09) !default; // 信息按钮禁用颜色
$-button-info-plain-border-color: rgba($-color-black, 0.45) !default; // 信息按钮禁用颜色
$-button-info-plain-bg-color: $-color-white !default; // 信息按钮禁用颜色
$-button-info-plain-disabled-bg-color: #F0F0F0 !default; // 信息按钮禁用颜色
$-button-info-plain-active-color: rgba($-color-black, 0.45) !default; // 信息按钮禁用颜色
$-button-info-plain-active-bg-color: #F0F0F0 !default; // 信息按钮禁用颜色
$-button-info-plain-normal-color: rgba($-color-black, 0.85) !default; // 信息幽灵按钮默认颜色
$-button-warning-color: $-color-warning !default; // 警告按钮颜色
$-button-warning-active-color: mix($-color-black, $-button-warning-color, 18%) !default; // 警告按钮点击颜色
$-button-warning-disabled-color: mix($-color-white, $-button-warning-color, 42%) !default; // 警告按钮禁用颜色
$-button-warning-box-shadow-color: rgba($-color-warning, 0.25) !default; // 主要按钮阴影颜色
$-button-error-color: $-color-danger !default; // 错误按钮颜色
$-button-error-active-color: mix($-color-black, $-button-error-color, 18%) !default; // 错误按钮点击颜色
$-button-error-disabled-color: mix($-color-white, $-button-error-color, 42%) !default; // 错误按钮禁用颜色
$-button-error-box-shadow-color: rgba($-color-danger, 0.25) !default; // 主要按钮阴影颜色
$-button-suck-height: 50px !default; // suck 类型按钮高度
$-button-suck-active-color: $-button-primary-plain-active-bg-color !default; // 错误按钮禁用颜色
/* cell */
$-cell-padding: $-size-side-padding !default; // cell 左右padding距离
$-cell-ling-height: 1.43 !default; // 行高
$-cell-group-title-fs: $-fs-title !default; // 组标题字号
$-cell-group-padding: 13px $-cell-padding !default; // 组padding
$-cell-group-title-color: rgba($-color-black, 0.85) !default; // 组标题文字颜色
$-cell-group-value-fs: $-fs-content !default; // 组值字号
$-cell-group-value-color: $-color-content !default; // 组值文字颜色
$-cell-wrapper-padding: 13px !default; // cell 容器padding
$-cell-wrapper-padding-with-label: 16px !default; // cell 容器上下padding有label情况下
$-cell-icon-right: $-cell-padding !default; // 图标距离右边缘
$-cell-icon-size: 16px !default; // 图标大小
$-cell-title-fs: 14px !default; // 标题字号
$-cell-title-color: rgba($-color-black, 0.85) !default; // 标题文字颜色
$-cell-label-fs: 12px !default; // 描述信息字号
$-cell-label-color: rgba($-color-black, 0.45) !default; // 描述信息文字颜色
$-cell-value-fs: 14px !default; // 右侧内容字号
$-cell-value-color: rgba($-color-black, 0.85) !default; // 右侧内容文字颜色
$-cell-value-line-height: 1.58 !default; // 右侧内容行高
$-cell-arrow-size: 18px !default; // 右箭头大小
$-cell-arrow-color: rgba($-color-black, 0.25) !default; // 右箭头颜色
$-cell-tap-bg: rgba($-color-black, 0.06) !default; // 点击态背景色
$-cell-title-fs-large: 16px !default; // 大尺寸标题字号
$-cell-label-fs-large: 14px !default; // 描述信息字号
$-cell-icon-size-large: 18px !default; // 图标大小
$-cell-required-color: $-color-danger !default; // 要求必填*颜色
$-cell-required-size: 18px !default; // 必填*字号
$-cell-vertical-top: 16px !default; // 表单类型-上下结构的间距
/* calendar */
$-calendar-fs: 16px !default;
$-calendar-panel-padding: 0 12px !default;
$-calendar-panel-title-fs: 14px !default;
$-calendar-panel-title-color: rgba(0, 0, 0, 0.85) !default;
$-calendar-week-color: rgba(0, 0, 0, 0.85) !default;
$-calendar-week-height: 36px !default;
$-calendar-week-fs: 12px !default;
$-calendar-day-fs: 16px !default;
$-calendar-day-color: rgba(0, 0, 0, 0.85) !default;
$-calendar-day-fw: 500 !default;
$-calendar-day-height: 64px !default;
$-calendar-month-width: 50px !default;
$-calendar-active-color: $-color-theme !default;
$-calendar-disabled-color: rgba(0, 0, 0, 0.25) !default;
$-calendar-range-color: rgba($-color-theme, 0.09) !default;
$-calendar-active-border: 8px !default;
$-calendar-info-fs: 10px !default;
/* checkbox */
$-checkbox-margin: 10px !default; // 多个复选框距离
$-checkbox-bg: $-color-white !default; // 多个复选框距离
$-checkbox-label-margin: 9px !default; // 右侧文字与左侧图标距离
$-checkbox-size: 16px !default; // 左侧图标尺寸
$-checkbox-icon-size: 14px !default; // 左侧图标尺寸
$-checkbox-border-color: #dcdcdc !default; // 左侧图标边框颜色
$-checkbox-check-color: $-color-white !default; // 左侧图标边框颜色
$-checkbox-label-fs: 14px !default; // 右侧文字字号
$-checkbox-label-color: rgba($-color-black, 0.85); // 右侧文字颜色
$-checkbox-checked-color: $-color-theme !default; // 选中颜色
$-checkbox-disabled-color: rgba($-color-black, 0.04) !default; // 禁用背景颜色
$-checkbox-disabled-label-color: rgba($-color-black, 0.25) !default; // 禁用文字颜色
$-checkbox-disabled-check-color: rgba($-color-black, 0.15) !default; // 禁用图标颜色
$-checkbox-disabled-check-bg: rgba($-color-black, 0.15) !default; // 禁用边框背景颜色
$-checkbox-square-radius: 4px !default; // 方型圆角大小
$-checkbox-large-size: 18px !default; // 左侧图标尺寸
$-checkbox-large-label-fs: 16px !default; // 右侧文字字号
$-checkbox-button-height: 32px !default; // 按钮模式复选框高
$-checkbox-button-min-width: 78px !default; // 按钮模式最小宽
$-checkbox-button-radius: 16px !default; // 按钮圆角大小
$-checkbox-button-bg: rgba($-color-black, 0.04) !default; // 按钮模式背景颜色
$-checkbox-button-font-size: 14px !default; // 按钮模式字号
$-checkbox-button-border: #f5f5f5 !default; // 按钮边框颜色
$-checkbox-button-disabled-border: rgba($-color-black, 0.15) !default; // 按钮禁用边框颜色
/* collapse */
$-collapse-side-padding: $-size-side-padding !default; // 左右间距
$-collapse-body-padding: 14px 25px !default; // body padding
$-collapse-header-padding: 13px $-size-side-padding !default; // 头部padding
$-collapse-title-color: rgba($-color-black, 0.85) !default; // 标题颜色
$-collapse-title-fs: 16px !default; // 标题字号
$-collapse-arrow-size: 18px !default; // 箭头大小
$-collapse-arrow-color: #d8d8d8 !default; // 箭头颜色
$-collapse-body-fs: 14px !default; // 内容字号
$-collapse-body-color: rgba($-color-black, 0.65) !default; // 内容颜色
$-collapse-disabled-color: rgba($-color-black, 0.15) !default; // 禁用颜色
$-collapse-retract-fs: 14px !default; // 更多 字号
$-collapse-more-color: $-color-theme !default; // 更多 颜色
/* divider */
$-divider-padding: 0 $-size-side-padding !default; // 两边间距
$-divider-color: rgba($-color-black, 0.45) !default; // 字体颜色
$-divider-line-color: rgba($-color-black, 0.15) !default; // 线条颜色
$-divider-fs: 14px !default; // 字体大小
/* drop-menu */
$-drop-menu-height: 48px; // 展示选中项的高度
$-drop-menu-color: $-color-content !default; // 展示选中项的颜色
$-drop-menu-fs: $-fs-content !default; // 展示选中项的字号
$-drop-menu-side-padding: $-size-side-padding !default; // 两边留白间距
$-drop-menu-disabled-color: rgba($-color-black, 0.25) !default; // 禁用颜色
$-drop-menu-item-height: 48px; // 选项高度
$-drop-menu-item-color: $-color-content !default; // 选项颜色
$-drop-menu-item-fs: $-fs-content !default; // 选项字号
$-drop-menu-item-color-active: $-color-theme !default; // 选中颜色
$-drop-menu-item-color-tip: rgba($-color-black, 0.45) !default; // 提示文字颜色
$-drop-menu-item-fs-tip: $-fs-secondary !default; // 提示文字字号
$-drop-menu-option-check-size: 20px !default; // check 图标大小
$-drop-menu-line-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light",
rgba(81, 124, 240, 1) rgba(118, 158, 245, 1),
0% 100%
) !default; // 下划线颜色
$-drop-menu-line-height: 3px !default; // 下划线高度
/* input-number */
$-input-number-color: #262626 !default; // 文字颜色
$-input-number-border-color: #e8e8e8 !default; // 边框颜色
$-input-number-disabled-color: rgba($-color-black, 0.25) !default; // 禁用颜色
$-input-number-height: 24px !default; // 加减号按钮高度
$-input-number-btn-width: 26px !default; // 加减号按钮宽度
$-input-number-input-width: 36px !default; // 输入框宽度
$-input-number-radius: 4px !default; // 加减号按钮圆角大小
$-input-number-fs: 12px !default; // 输入框字号
$-input-number-icon-size: 14px !default; // 加减号图标大小
$-input-number-icon-color: rgba($-color-black, 0.65) !default; // icon颜色
/* input */
$-input-border-color: #dadada !default; // 无label边框颜色
$-input-not-empty-border-color: #262626 !default; // 无label边框颜色
$-input-fs: $-cell-title-fs !default; // 字号
$-input-fs-large: $-cell-title-fs-large !default; // 大尺寸字号
$-input-icon-margin: 8px !default; // 图标距离
$-input-color: #262626 !default; // 文字颜色
$-input-placeholder-color: #bfbfbf !default; // 占位符颜色
$-input-disabled-color: #d9d9d9 !default; // 输入框禁用颜色
$-input-error-color: $-color-danger !default; // 输入框错误颜色
$-input-icon-color: #bfbfbf !default; // 图标颜色
$-input-clear-color: #585858 !default; // 关闭按钮颜色
$-input-count-color: #bfbfbf !default; // 计数文字颜色
$-input-count-current-color: #262626 !default; // 当前长度颜色
$-input-label-padding: 11px 10px 11px 0 !default; // label padding
$-input-bg: $-color-white !default; // 默认背景颜色
$-input-cell-bg: $-color-white !default; // cell 类型背景色
$-input-cell-border-color: $-color-border-light !default; // cell 类型边框颜色
$-input-cell-padding: $-size-side-padding !default; // cell 类型左右间距
$-input-cell-height: 46px !default; // cell 高度
$-input-cell-height-large: 48px !default; // cell 大尺寸高度
$-input-cell-label-width: 33% !default; // cell label 的宽度
$-input-inner-height: 34px !default; // 非cell和textarea下的高度
$-input-inner-height-no-border: 20px !default; // 无边框下的高度
$-input-inner-padding: 6px 0 !default; // 非cell和textarea下的padding
$-input-count-fs: 14px !default; // 计数字号
$-input-count-fs-large: 14px !default; // 大尺寸计数字号
$-input-icon-size: 16px !default; // 图标大小
$-input-icon-size-large: 18px !default; // 大尺寸图标大小
$-input-textarea-padding: 15px !default; // textarea下的padding
/* loadmore */
$-loadmore-height: 48px !default; // 高度
$-loadmore-color: rgba($-color-black, 0.45) !default; // 颜色
$-loadmore-fs: 14px !default; // 字号
$-loadmore-error-color: $-color-theme !default; // 点击重试颜色
/* message-box */
$-message-box-width: 300px !default; // 宽度
$-message-box-bg: $-color-white !default; // 默认背景颜色
$-message-box-radius: 16px !default; // 圆角大小
$-message-box-padding: 25px 24px 0 !default; // 主体内容padding
$-message-box-title-fs: 16px !default; // 标题字号
$-message-box-title-color: rgba($-color-black, 0.85) !default; // 标题颜色
$-message-box-content-fs: 14px !default; // 内容字号
$-message-box-content-color: #666666 !default; // 内容颜色
$-message-box-content-max-height: 264px !default; // 内容最大高度
$-message-box-content-scrollbar-width: 4px !default; // 内容滚动条宽度
$-message-box-content-scrollbar-color: rgba($-color-black, 0.1) !default; // 内容滚动条颜色
$-message-box-input-error-color: $-input-error-color !default; // 输入框错误颜色
/* notice-bar */
$-notice-bar-fs: 12px !default; // 字号
$-notice-bar-line-height: 18px !default; // 行高
$-notice-bar-border-radius: 8px !default; // 圆角
$-notice-bar-padding: 9px 20px 9px 15px !default; // 非换行下的padding
$-notice-bar-warning-bg: #fff6c8 !default; // 背景色
$-notice-bar-info-bg: #f4f9ff !default; // 背景色
$-notice-bar-danger-bg: #feeced !default; // 背景色
$-notice-bar-warning-color: $-color-warning !default; // 文字和图标颜色
$-notice-bar-info-color: $-color-theme !default; // 文字和图标颜色
$-notice-bar-danger-color: $-color-danger !default; // 文字和图标颜色
$-notice-bar-prefix-size: 18px !default; // 图标大小
$-notice-bar-close-bg: rgba($-color-black, 0.15) !default; // 右侧关闭按钮背景颜色
$-notice-bar-close-size: 18px !default; // 右侧关闭按钮背景颜色
$-notice-bar-close-color: $-color-white !default; // 右侧关闭按钮颜色
$-notice-bar-close-size: 10px !default; // 关闭按钮大小
$-notice-bar-wrap-padding: 14px $-size-side-padding !default; // 换行下的padding
/* pagination */
$-pagination-content-padding: 10px 15px !default;
$-pagination-message-padding: 1px 0 16px 0 !default;
$-pagination-message-fs: 12px !default;
$-pagination-message-color: rgba($-color-black, 0.69) !default;
$-pagination-nav-border: 1px solid rgba($-color-black, 0.45) !default;
$-pagination-nav-border-radius: 16px !default;
$-pagination-nav-fs: 12px !default;
$-pagination-nav-width: 60px !default;
$-pagination-nav-color: rgba($-color-black, 0.85) !default;
$-pagination-nav-content-fs: 12px !default;
$-pagination-nav-sepatator-padding: 0 4px !default;
$-pagination-nav-current-color: $-color-theme !default;
/* picker */
$-picker-toolbar-height: 54px !default; // toolbar 操作条的高度
$-picker-action-height: 16px !default; // toolbar 操作条的高度
$-picker-toolbar-finish-color: $-color-theme !default; // toolbar 操作条完成按钮的颜色
$-picker-toolbar-cancel-color: #666666 !default; // toolbar 操作条的边框颜色
$-picker-toolbar-fs: $-fs-title !default; // toolbar 操作条的字号
$-picker-toolbar-title-color: rgba($-color-black, 0.85) !default; // toolbar 操作台的标题颜色
$-picker-column-fs: 16px !default; // 选择器选项的字号
$-picker-bg: $-color-white !default; // 选择器选项的字号
$-picker-column-active-fs: 18px !default; // 选择器选项被选中的字号
$-picker-column-color: rgba($-color-black, 0.85) !default; // 选择器选项的颜色
$-picker-column-height: 210px !default; // 列高 滚筒外部的高度
$-picker-column-item-height: 35px !default; // 列高 滚筒外部的高度
$-picker-column-select-bg: #f5f5f5 !default;
$-picker-loading-button-color: rgba($-color-black, 0.25) !default; // loading 背景颜色
$-picker-column-padding: 0 $-size-side-padding !default; // 选项内间距
$-picker-column-disabled-color: rgba($-color-black, 0.25) !default; // 选择器选项禁用的颜色
$-picker-mask: linear-gradient(180deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.25)),
linear-gradient(0deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.25)) !default; // 上下阴影
$-picker-loading-bg: rgba($-color-white, 0.8) !default; // loading 背景颜色
$-picker-region-separator-color: rgba($-color-black, 0.65) !default; // 区域选择文字颜色
$-picker-cell-arrow-size-large: $-cell-icon-size !default; // cell 类型的大尺寸 右侧icon尺寸
$-picker-region-color: rgba($-color-black, 0.45) !default; // 区域选择文字颜色
$-picker-region-bg-active-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light" "light",
rgba(79,124,248,1) rgba(102,141,248,1) rgba(102,141,248,1),
0% 100% 100%
) !default; // 区域选择激活选中背景颜色
$-picker-region-fs: 14px !default; // 区域选择文字字号
/* col-picker */
$-col-picker-selected-height: 44px !default; // 弹框顶部值高度
$-col-picker-selected-padding: 0 16px !default; // 弹框顶部值左右间距
$-col-picker-selected-fs: 14px !default; // 弹框顶部值字号
$-col-picker-selected-color: rgba($-color-black, 0.85) !default; // 弹框顶部值文字颜色
$-col-picker-selected-fw: 700 !default; // 弹框顶部值高亮字重
$-col-picker-line-width: 16px !default; // 弹框顶部值高亮线条宽度
$-col-picker-line-height: 3px !default; // 弹框顶部值高亮线条高度
$-col-picker-line-color: linear-gradient(315deg, rgba(81,124,240,1), rgba(118,158,245,1)) !default; // 弹框顶部值高亮线条颜色
$-col-picker-line-box-shadow: 0px 1px 2px 0px rgba(1, 87, 255, 0.2) !default; // 弹框顶部值高亮线条阴影
$-col-picker-list-height: 53vh !default; // 弹框列表高度
$-col-picker-list-padding-bottom: 30px !default; // 弹框列表底部间距
$-col-picker-list-color: rgba($-color-black, 0.85) !default; // 弹框列表文字颜色
$-col-picker-list-color-disabled: rgba($-color-black, 0.15) !default; // 弹框列表文字禁用颜色
$-col-picker-list-color-tip: rgba($-color-black, 0.45) !default; // 弹框列表提示文字颜色
$-col-picker-list-fs: 14px !default; // 弹框列表文字字号
$-col-picker-list-fs-tip: 12px !default; // 弹框列表提示文字字号
$-col-picker-list-item-padding: 12px 15px !default; // 弹框列表选项间距
$-col-picker-list-checked-icon-size: 18px !default; // 弹框列表选中箭头大小
$-col-picker-list-color-checked: $-color-theme !default; // 弹框列表选中选项颜色
/* popup */
$-popup-close-size: 24px !default; // 关闭按钮尺寸
$-popup-close-color: #666 !default; // 关闭按钮颜色
/* progress */
$-progress-padding: 9px 0 8px !default; // 进度条内边距
$-progress-bg: rgba(229,229,229,1) !default; // 进度条底色
$-progress-danger-color: $-color-danger !default; // 进度条danger颜色
$-progress-success-color: $-color-success !default; // 进度条success进度条颜色
$-progress-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light",
#517CF0 #769EF5 ,
0% 100%
) !default; // 进度条渐变色
$-progress-linear-success-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light",
#20B080 #2BD69D ,
0% 100%
) !default; // success进度条渐变色
$-progress-linear-danger-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light",
#E04350 #FF5964 ,
0% 100%
) !default; // danger进度条渐变色
$-progress-height: 3px !default; // 进度条高度
$-progress-label-color: #333 !default; // 文字颜色
$-progress-label-fs: 14px !default; // 文字字号
$-progress-icon-fs: 18px !default; // 图标字号
/* radio */
$-radio-margin: $-checkbox-margin !default; // 多个单选框距离
$-radio-label-margin: $-checkbox-label-margin !default; // 右侧文字与左侧图标距离
$-radio-size: 16px !default; // 左侧图标尺寸
$-radio-bg: $-checkbox-bg !default; // 左侧图标尺寸
$-radio-label-fs: $-checkbox-label-fs !default; // 右侧文字字号
$-radio-label-color: $-checkbox-label-color !default; // 右侧文字颜色
$-radio-checked-color: $-checkbox-checked-color !default; // 选中颜色
$-radio-disabled-color: $-checkbox-disabled-color !default; // 禁用颜色
$-radio-disabled-label-color: $-checkbox-disabled-label-color !default; // 禁用文字颜色
$-radio-large-size: $-checkbox-large-size !default; // 左侧图标尺寸
$-radio-large-label-fs: $-checkbox-large-label-fs !default; // 右侧文字字号
$-radio-button-height: $-checkbox-button-height !default; // 按钮模式复选框高
$-radio-button-min-width: 60px !default; // 按钮模式最小宽
$-radio-button-max-width: 144px !default; // 按钮模式最大宽
$-radio-button-radius: $-checkbox-button-radius !default; // 按钮圆角大小
$-radio-button-bg: $-checkbox-button-bg !default; // 按钮模式背景颜色
$-radio-button-fs: $-checkbox-button-font-size !default; // 按钮模式字号
$-radio-button-border: $-checkbox-button-border !default; // 按钮边框颜色
$-radio-button-disabled-border: $-checkbox-button-disabled-border !default; // 按钮禁用边框颜色
$-radio-dot-size: 8px !default; // 单选dot模式圆点尺寸
$-radio-dot-large-size: 10px !default; // 单选dot模式大尺寸圆点尺寸
$-radio-dot-checked-bg: $-color-theme !default; // 单选dot模式选中背景色
$-radio-dot-checked-border-color: $-color-theme !default; // 单选dot模式选中边框色
$-radio-dot-border-color: #dcdcdc !default; // 单选dot模式边框色
$-radio-dot-disabled-border: #d9d9d9 !default; // 单选dot模式禁用边框颜色
$-radio-dot-disabled-bg: #d9d9d9 !default; // 单选dot模式禁用背景颜色
/* search */
$-search-side-padding: $-size-side-padding !default; // 左右间距
$-search-padding: 10px 0 10px $-search-side-padding !default; // 不包含取消按钮的间距
$-search-input-radius: 15px !default; // 输入框圆角大小
$-search-input-bg: $-color-bg !default; // 输入框背景色
$-search-input-height: 30px !default; // 输入框高度
$-search-input-padding: 0 32px 0 42px !default; // 输入框间距
$-search-input-fs: $-fs-content !default; // 输入框字号
$-search-input-color: #262626 !default; // 输入框文字颜色
$-search-icon-color: $-color-icon !default; // 图标颜色
$-search-placeholder-color: #bfbfbf !default; // placeholder 颜色
$-search-cancel-padding: 0 $-search-side-padding 0 10px !default; // 取消按钮间距
$-search-cancel-fs: $-fs-title !default; // 取消按钮字号
$-search-cancel-color: rgba($-color-black, 0.65) !default; // 取消按钮颜色
$-search-light-bg: $-color-bg !default; // light 类型的容器背景色
/* slider */
$-slider-fs: $-fs-content !default; // 字体大小
$-slider-handle-radius: 12px !default; // 滑块半径
$-slider-handle-bg: resultColor(
$open-linear,
139deg,
$-color-theme,
"dark" "light",
#FFFFFF #F7F7F7 ,
0% 100%
) !default; // 滑块背景
$-slider-axie-height: 3px !default; // 滑轴高度
$-slider-color: #333 !default; // 字体颜色
$-slider-axie-bg: #E5E5E5 !default; // 滑轴的默认背景色
$-slider-line-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light",
#517CF0 #769EF5 ,
0% 100%
) !default; // 进度条颜色
$-slider-disabled-color: rgba($-color-black, 0.25) !default; // 禁用状态下字体颜色
/* sort-button */
$-sort-button-fs: $-fs-content !default; // 字号
$-sort-button-color: $-color-content !default; // 颜色
$-sort-button-height: 48px !default; // 高度
$-sort-button-line-height: 3px !default; // 下划线高度
$-sort-button-line-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light",
rgba(81, 124, 240, 1) rgba(118, 158, 245, 1),
0% 100%
) !default; // 下划线颜色
/* steps */
$-steps-icon-size: 22px !default; // 图标尺寸
$-steps-inactive-color: rgba($-color-black, 0.25) !default; // 等待状态文字颜色
$-steps-finished-color: $-color-theme !default; // 完成文字颜色
$-steps-icon-text-fs: $-fs-content !default; // 数字图标文字字号
$-steps-error-color: $-color-danger !default; // 异常颜色
$-steps-title-fs: $-fs-content !default; // 标题字号
$-steps-title-fw: $-fw-medium !default; // 标题字重
$-steps-label-fs: $-fs-secondary !default; // 描述信息字号
$-steps-description-color: rgba($-color-black, 0.45) !default; // 描述信息颜色
$-steps-is-icon-width: 30px !default; // 自定义图标的宽度给左右留白
$-steps-line-color: rgba($-color-black, 0.15) !default; // 线条颜色
$-steps-dot-size: 7px !default; // 点状大小
$-steps-dot-active-size: 9px !default; // 点状高亮大小
/* switch */
$-switch-width: 51px !default; // 宽度
$-switch-height: 32px !default; // 高度
$-switch-circle-size: 28px !default; // 圆点大小
$-switch-border-color: #e5e5e5 !default; // 边框颜色选中状态背景颜色
$-switch-active-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"light" "dark",
#4f7cf8 #668df8,
0% 100%
) !default; // 选中状态背景
$-switch-active-shadow-color: rgba(0, 83, 162, 0.5) !default; // 选中状态shadow颜色
$-switch-inactive-color: resultColor(
$open-linear,
315deg,
#dadada,
"light" "dark" "dark",
#f2f2f2 #dadada #668df8,
0% 100% 100%
) !default; // 非选中背景颜色
$-switch-inactive-shadow-color: rgba(155, 155, 155, 0.5) !default; // 非选中状态shadow颜色
/* tabs */
$-tabs-nav-arrow-fs: 18px !default; // 全部Icon字号
$-tabs-nav-arrow-open-fs: 14px !default; // 展开Icon字号
$-tabs-nav-width: 100vw; // tabs 头部切换宽度
$-tabs-nav-height: 42px !default; // 头部切换高度
$-tabs-nav-fs: $-fs-content !default; // 头部切换文字大小
$-tabs-nav-color: rgba($-color-black, 0.85) !default; // 头部切换文字颜色
$-tabs-nav-bg: $-color-white !default; // 背景颜色
$-tabs-nav-active-color: $-color-theme !default; // 头部高亮颜色
$-tabs-nav-disabled-color: rgba($-color-black, 0.25) !default; // 头部禁用颜色
$-tabs-nav-line-height: 3px !default; // 高亮边框高度
$-tabs-nav-line-bg-color: resultColor(
$open-linear,
315deg,
$-color-theme,
"dark" "light",
rgba(81, 124, 240, 1) rgba(118, 158, 245, 1),
0% 100%
) !default; // 底部条颜色
$-tabs-nav-map-fs: $-fs-content !default; // map 类型按钮字号
$-tabs-nav-map-color: rgba($-color-black, 0.85) !default; // map 类型按钮文字颜色
$-tabs-nav-map-arrow-color: rgba($-color-black, 0.65) !default; // map 类型箭头颜色
$-tabs-nav-map-btn-before-bg: linear-gradient(
270deg,
rgba(255, 255, 255, 1) 1%,
rgba(255, 255, 255, 0) 100%
) !default; // 左侧map遮罩阴影
$-tabs-nav-map-button-back-color: rgba($-color-black, 0.04) !default; // map 类型按钮边框颜色
$-tabs-nav-map-button-radius: 16px !default; // map 类型按钮圆角大小
$-tabs-nav-map-modal-bg: $-modal-bg !default; // map 类型蒙层背景色
/* tag */
$-tag-fs: $-fs-secondary !default; // 字号
$-tag-color: $-color-white !default; // 字体颜色
$-tag-small-fs: $-fs-aid !default; // 小尺寸字号
$-tag-info-color: #585858 !default; // info 颜色
$-tag-primary-color: $-color-theme !default; // 主颜色
$-tag-danger-color: rgba(250,67,80,1) !default; // danger 颜色
$-tag-warning-color: rgba(255,144,0,1) !default; // warning 颜色
$-tag-success-color: rgba(51,187,68,1) !default; // success 颜色
$-tag-info-bg: resultColor(
$open-linear,
49deg,
$-color-black,
"dark" "light",
#808080 #999999,
0% 100%
) !default; // info 背景颜色
$-tag-primary-bg: resultColor(
$open-linear,
49deg,
$-color-theme,
"dark" "light",
rgba(81,124,240,1) rgba(118,158,245,1),
0% 100%
) !default; // 主背景颜色
$-tag-danger-bg: resultColor(
$open-linear,
44deg,
$-color-danger,
"dark" "light",
rgba(230,62,70,1) rgba(255,64,73,1),
0% 100%
) !default; // danger 背景颜色
$-tag-warning-bg: resultColor(
$open-linear,
45deg,
$-color-warning,
"dark" "light",
rgba(230,113,55,1) rgba(255,151,76,1),
0% 100%
) !default; // warning 背景颜色
$-tag-success-bg: resultColor(
$open-linear,
45deg,
$-color-success,
"dark" "light",
rgba(32,176,128,1) rgba(43,214,157,1),
0% 100%
) !default; // success 背景颜色
$-tag-round-color: rgba(102, 102, 102, 1) !default; // round 字体颜色
$-tag-round-border-color: rgba(225,225,225,1) !default; // round 边框颜色
$-tag-round-radius: 12px !default; // round 圆角大小
$-tag-mark-radius: 6px 2px 6px 2px !default; // mark 圆角大小
$-tag-close-size: 14px !default; // 关闭按钮字号
$-tag-close-color: $-tag-info-color !default; // 关闭按钮颜色
$-tag-close-active-color: rgba($-color-black, 0.45) !default; // 关闭按钮 active 颜色
/* toast */
$-toast-padding: 16px 24px !default; // padding
$-toast-max-width: 300px !default; // 最大宽度
$-toast-radius: 8px !default; // 圆角大小
$-toast-bg: $-modal-bg !default; // 背景色
$-toast-fs: $-fs-content !default; // 字号
$-toast-with-icon-min-width: 150px !default; // 有图标的情况下最小宽度
$-toast-icon-size: 39px !default; // 图标大小
$-toast-loading-padding: 10px !default; // loading 下的padding
$-toast-box-shadow: 0px 6px 16px 0px rgba($-color-black, 0.08) !default; // 外部阴影
/* tooltip */
$-tooltip-bg: rgba(38, 39, 40, 0.8) !default; // 背景色
$-tooltip-color: $-color-white !default; // 文字颜色
$-tooltip-radius: 8px !default; // 圆角大小
$-tooltip-arrow-size: 9px !default; // 箭头大小
$-tooltip-fs: $-fs-content !default; // 字号
$-tooltip-blur: 10px !default; // 背景高斯模糊效果
$-tooltip-padding: 9px 20px !default; // 间距
$-tooltip-close-size: 6px !default; // 背景高斯模糊效果
$-tooltip-z-index: 500 !default;
$-tooltip-line-height: 18px !default; // 行高
/* popover */
$-popover-bg: $-color-white !default; // 背景色
$-popover-color: rgba($-color-black, 0.85) !default; // 文字颜色
$-popover-box-shadow: 0px 2px 10px 0px rgba($-color-black, 0.1) !default; // 阴影颜色
$-popover-arrow-box-shadow: 0px 2px 10px 0px rgba($-color-black, 0.2) !default; // 阴影颜色
$-popover-border-color: rgba($-color-black, 0.09) !default; // 阴影颜色
$-popover-radius: 4px !default; // 圆角大小
$-popover-arrow-size: 6px !default; // 箭头大小
$-popover-fs: $-fs-content !default; // 字号
$-popover-padding: 15px !default; // 间距
$-popover-line-height: 18px !default; // 行高
$-popover-z-index: $-tooltip-z-index !default;
/* grid-item */
$-grid-item-fs: 12px !default; // 字号
$-grid-item-bg: $-color-white !default; // 字号
$-grid-item-padding: 14px 0px !default; // 内容的 padding
$-grid-item-border-color: $-color-border-light !default; // 边框颜色
/* statustip */
$-statustip-fs: $-fs-content !default; // 字号
$-statustip-color: rgba($-color-black, 0.45) !default; // 文字颜色
$-statustip-line-height: 16px !default; // 文字行高
$-statustip-padding: 5px 10px !default; // 间距
/* card */
$-card-bg: $-color-white !default; // 背景色
$-card-fs: $-fs-content !default; // 卡片字号
$-card-padding: 0 $-size-side-padding !default; // 内边距
$-card-footer-padding: 12px 0 16px !default; // 底部内边距
$-card-shadow-color: 0px 4px 8px 0px rgba($-color-black, 0.02) !default; // 阴影
$-card-radius: 8px !default; // 圆角大小
$-card-line-height: 1.1 !default; // 行高
$-card-margin: 0 $-size-side-padding !default; // 外边距
$-card-title-color: rgba($-color-black, 0.85) !default; // 标题颜色
$-card-title-fs: $-fs-title !default; // 矩形卡片标题字号
$-card-content-border-color: rgba($-color-black, 0.09) !default; // 内容边框
$-card-rectangle-title-padding: 15px 15px 12px !default; // 矩形卡片头部内边距
$-card-rectangle-content-padding: 16px 0 !default; // 矩形卡片内容内边距
$-card-rectangle-footer-padding: 12px 0 !default; // 矩形卡片底部内边距
$-card-content-color: rgba($-color-black, 0.45) !default; // 文本内容颜色
$-card-content-line-height: 1.428 !default; // 文本内容行高
$-card-content-margin: 13px 0 12px !default; // 内容外边距
$-card-content-rectangle-margin: 14px 0 12px !default; // 矩形卡片内容外边距
/* upload */
$-upload-size: 80px !default; // upload的外边框默认尺寸
$-upload-evoke-icon-size: 32px !default; // 唤起项的图标大小
$-upload-evoke-bg: rgba($-color-black, 0.04) !default; // 唤起项的背景色
$-upload-evoke-color: rgba($-color-black, 0.25) !default; // 唤起项的图标颜色
$-upload-evoke-disabled-color: rgba($-color-black, 0.09) !default; // 唤起项禁用颜色
$-upload-close-icon-size: 16px !default; // 移除按钮尺寸
$-upload-close-icon-color: rgba($-color-black, 0.65) !default; // 移除按钮颜色
$-upload-progress-fs: 14px !default; // 进度文字字号
$-upload-preview-name-fs: 12px !default; // 预览图片名字号
$-upload-preview-icon-size: 24px !default; // 预览内部图标尺寸
$-upload-preview-name-bg: rgba($-color-black, 0.6) !default; // 预览文件名背景色
$-upload-preview-name-height: 22px !default; // 预览文件名背景高度
/* curtain */
$-curtain-content-radius: 24px !default; // 内容圆角
$-curtain-content-close-color: $-color-white !default; // 关闭按钮颜色
$-curtain-content-close-fs: $-fs-big !default; // 关闭按钮大小

View File

@ -0,0 +1,29 @@
const _b64chars = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/']
const _mkUriSafe = (src) => src.replace(/[+/]/g, (m0) => (m0 === '+' ? '-' : '_')).replace(/=+$/m, '')
const fromUint8Array = (src, rfc4648 = false) => {
let b64 = ''
for (let i = 0, l = src.length; i < l; i += 3) {
const [a0, a1, a2] = [src[i], src[i + 1], src[i + 2]]
const ord = (a0 << 16) | (a1 << 8) | a2
b64 += _b64chars[ord >>> 18]
b64 += _b64chars[(ord >>> 12) & 63]
b64 += typeof a1 !== 'undefined' ? _b64chars[(ord >>> 6) & 63] : '='
b64 += typeof a2 !== 'undefined' ? _b64chars[ord & 63] : '='
}
return rfc4648 ? _mkUriSafe(b64) : b64
}
const _btoa =
typeof btoa === 'function'
? (s) => btoa(s)
: (s) => {
if (s.charCodeAt() > 255) {
throw new RangeError('The string contains invalid characters.')
}
return fromUint8Array(Uint8Array.from(s, (c: any) => c.charCodeAt(0)))
}
const utob = (src) => unescape(encodeURIComponent(src))
export default function encode(src, rfc4648 = false) {
const b64 = _btoa(utob(src))
return rfc4648 ? _mkUriSafe(b64) : b64
}

View File

@ -0,0 +1,27 @@
let queue = []
let outsideId = 1
export function pushToQueue(comp) {
comp.outsideId = ++outsideId
queue.push(comp)
}
export function removeFromQueue(comp) {
queue = queue.filter((item) => {
return item.outsideId !== comp.outsideId
})
}
export function closeOther(comp) {
queue.forEach((item) => {
if (item.outsideId !== comp.outsideId) {
item.close()
}
})
}
export default function closeOutside() {
queue.forEach((item) => {
item.close()
})
}

View File

@ -0,0 +1,53 @@
### 使用方法
```javascript
import VueComponent from '/packages/common/component.js'
VueComponent({
props: {},
beforeCreate(){},
created(){},
mounted(){},
destroyed(){}
})
```
### 映射关系
>如果使用封装的`VueComponent`组件,以下小程序`原生属性/方法`必须替换为对应的`封装属性/方法`
| 封装 | 原生 |
|--------------|----------- |
| props | properties |
| beforeCreate | created |
| created | attached |
| mounted | ready |
| destroyed | detached |
### 全局样式类
* 使用VueComponent组件时会自动添加custom-class类外部可以通过此类修改组件内部的样式。
```html
<view>
<wd-button custom-class="custom">确定</wd-button>
</view>
```
```css
.custom{
background-color: red;
}
```
### 原生生命周期
> 必须要弄清楚的声明周期,执行顺序按照降序排列。
| hook | 描述 |
|-------------------------|----------- |
| properties | 类型vue的props。 |
| data | 类型vue的data。 |
| created | 在组件实例刚刚被创建时执行此时可以拿到data但是拿不到props的最新值只能拿到默认值不能调用setData可以操作设置this。 |
| properties:observer | 如果实例初始化时给properties传值会触发observer先于attached执行 |
| attached | 在组件实例进入页面节点树时执行可以调用setData。 |
| relations | DOM build父组件建立连接双方可以在此hook互相操作对方。 |
| ready | DOM painted。 |

View File

@ -0,0 +1,2 @@
/* eslint-disable */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.dayjs=e()}(this,function(){"use strict";const t=60,e=60*t,s=24*e,r=7*s,n=(t,e,s)=>!t||t.length>=e?t:`${Array(e+1-t.length).join(s)}${t}`;class i{constructor(t){this.utc=!1;const e=this.parseConfig(t);this.date=new Date(e),this.timeZone=this.date.getTimezoneOffset()/60,this.timeZoneString=n(String(-1*this.timeZone).replace(/^(.)?(\d)/,"$10$200"),5,"+"),this.mYear=this.date.getFullYear(),this.mMonth=this.date.getMonth(),this.mDay=this.date.getDate(),this.mWeek=this.date.getDay(),this.mHour=this.date.getHours(),this.mMinute=this.date.getMinutes(),this.mSecond=this.date.getSeconds()}parseConfig(t){if(!t)return new Date;if(t instanceof Date)return t;if(/^(\d){8}$/.test(t)){return this.utc=!0,`${t.substr(0,4)}-${t.substr(4,2)}-${t.substr(6,2)}`}return t}year(){return this.mYear}month(){return this.mMonth}unix(){const t=this.utc?60*this.timeZone*60*1e3:0;return Math.floor((this.date.getTime()+t)/1e3)}toString(){return this.date.toUTCString()}startOf(t){switch(t){case"year":return new i(new Date(this.year(),0,1));case"month":return new i(new Date(this.year(),this.month(),1));default:return this}}add(n,a){let h;switch(a){case"m":case"minutes":h=t;break;case"h":case"hours":h=e;break;case"d":case"days":h=s;break;case"w":case"weeks":h=r;break;default:h=1}const u=this.unix()+n*h;return new i(1e3*u)}subtract(t,e){return this.add(-1*t,e)}format(t="YYYY-MM-DDTHH:mm:ssZ"){const e=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];return t.replace(/Y{2,4}|M{1,2}|D{1,2}|d{1,4}|H{1,2}|m{1,2}|s{1,2}|Z{1,2}/g,t=>{switch(t){case"YY":return String(this.mYear).slice(-2);case"YYYY":return String(this.mYear);case"M":return String(this.mMonth+1);case"MM":return n(String(this.mMonth+1),2,"0");case"D":return String(this.mDay);case"DD":return n(String(this.mDay),2,"0");case"d":return String(this.mWeek);case"dddd":return e[this.mWeek];case"H":return String(this.mHour);case"HH":return n(String(this.mHour),2,"0");case"m":return String(this.mMinute);case"mm":return n(String(this.mMinute),2,"0");case"s":return String(this.mSecond);case"ss":return n(String(this.mSecond),2,"0");case"Z":return`${this.timeZoneString.slice(0,-2)}:00`;case"ZZ":return this.timeZoneString;default:return t}})}}return t=>new i(t)});

View File

@ -0,0 +1,206 @@
import isObject from './isObject'
import root from './internal/root'
/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked, or until the next browser frame is drawn. The debounced function
* comes with a `cancel` method to cancel delayed `func` invocations and a
* `flush` method to immediately invoke them. Provide `options` to indicate
* whether `func` should be invoked on the leading and/or trailing edge of the
* `wait` timeout. The `func` is invoked with the last arguments provided to the
* debounced function. Subsequent calls to the debounced function return the
* result of the last `func` invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the debounced function
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until the next tick, similar to `setTimeout` with a timeout of `0`.
*
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
* invocation will be deferred until the next frame is drawn (typically about
* 16ms).
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `debounce` and `throttle`.
*
* @since 0.1.0
* @category Function
* @param {Function} func The function to debounce.
* @param {number} [wait=0]
* The number of milliseconds to delay; if omitted, `requestAnimationFrame` is
* used (if available).
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=false]
* Specify invoking on the leading edge of the timeout.
* @param {number} [options.maxWait]
* The maximum time `func` is allowed to be delayed before it's invoked.
* @param {boolean} [options.trailing=true]
* Specify invoking on the trailing edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* // Avoid costly calculations while the window size is in flux.
* jQuery(window).on('resize', debounce(calculateLayout, 150))
*
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
* jQuery(element).on('click', debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* }))
*
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
* const debounced = debounce(batchLog, 250, { 'maxWait': 1000 })
* const source = new EventSource('/stream')
* jQuery(source).on('message', debounced)
*
* // Cancel the trailing debounced invocation.
* jQuery(window).on('popstate', debounced.cancel)
*
* // Check for pending invocations.
* const status = debounced.pending() ? "Pending..." : "Ready"
*/
function debounce(func, wait, options) {
let lastArgs, lastThis, maxWait, result, timerId, lastCallTime
let lastInvokeTime = 0
let leading = false
let maxing = false
let trailing = true
// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
const useRAF = !wait && wait !== 0 && typeof root.requestAnimationFrame === 'function'
if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
wait = +wait || 0
if (isObject(options)) {
leading = !!options.leading
maxing = 'maxWait' in options
maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
trailing = 'trailing' in options ? !!options.trailing : trailing
}
function invokeFunc(time) {
const args = lastArgs
const thisArg = lastThis
lastArgs = lastThis = undefined
lastInvokeTime = time
result = func.apply(thisArg, args)
return result
}
function startTimer(pendingFunc, wait) {
if (useRAF) {
root.cancelAnimationFrame(timerId)
return root.requestAnimationFrame(pendingFunc)
}
return setTimeout(pendingFunc, wait)
}
function cancelTimer(id) {
if (useRAF) {
return root.cancelAnimationFrame(id)
}
clearTimeout(id)
}
function leadingEdge(time) {
// Reset any `maxWait` timer.
lastInvokeTime = time
// Start the timer for the trailing edge.
timerId = startTimer(timerExpired, wait)
// Invoke the leading edge.
return leading ? invokeFunc(time) : result
}
function remainingWait(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
const timeWaiting = wait - timeSinceLastCall
return maxing ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting
}
function shouldInvoke(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
// it as the trailing edge, or we've hit the `maxWait` limit.
return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || (maxing && timeSinceLastInvoke >= maxWait)
}
function timerExpired() {
const time = Date.now()
if (shouldInvoke(time)) {
return trailingEdge(time)
}
// Restart the timer.
timerId = startTimer(timerExpired, remainingWait(time))
}
function trailingEdge(time) {
timerId = undefined
// Only invoke if we have `lastArgs` which means `func` has been
// debounced at least once.
if (trailing && lastArgs) {
return invokeFunc(time)
}
lastArgs = lastThis = undefined
return result
}
function cancel() {
if (timerId !== undefined) {
cancelTimer(timerId)
}
lastInvokeTime = 0
lastArgs = lastCallTime = lastThis = timerId = undefined
}
function flush() {
return timerId === undefined ? result : trailingEdge(Date.now())
}
function pending() {
return timerId !== undefined
}
function debounced(...args) {
const time = Date.now()
const isInvoking = shouldInvoke(time)
lastArgs = args
lastThis = this
lastCallTime = time
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime)
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = startTimer(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) {
timerId = startTimer(timerExpired, wait)
}
return result
}
debounced.cancel = cancel
debounced.flush = flush
debounced.pending = pending
return debounced
}
export default debounce

View File

@ -0,0 +1,4 @@
/** Detect free variable `global` from Node.js. */
const freeGlobal = typeof global === 'object' && global !== null && global.Object === Object && global
export default freeGlobal

View File

@ -0,0 +1,14 @@
import freeGlobal from './freeGlobal.js'
/** Detect free variable `globalThis` */
// eslint-disable-next-line eqeqeq
const freeGlobalThis = typeof globalThis === 'object' && globalThis !== null && globalThis.Object == Object && globalThis
/** Detect free variable `self`. */
const freeSelf = typeof self === 'object' && self !== null && self.Object === Object && self
/** Used as a reference to the global object. */
// eslint-disable-next-line no-new-func
const root = freeGlobalThis || freeGlobal || freeSelf || Function('return this')()
export default root

View File

@ -0,0 +1,29 @@
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* isObject({})
* // => true
*
* isObject([1, 2, 3])
* // => true
*
* isObject(Function)
* // => true
*
* isObject(null)
* // => false
*/
function isObject(value) {
const type = typeof value
return value != null && (type === 'object' || type === 'function')
}
export default isObject

View File

@ -0,0 +1,180 @@
import debounce from './lodash/debounce'
/**
* @description 对num自动填充px
* @param {Number} num
* @return {string} num+px
*/
export function addUnit(num) {
return Number.isNaN(Number(num)) ? num : `${num}px`
}
/**
* @description 获取当前页面栈顶(当前显示的页面)
* @return {wx.Page}
*/
export function getContext() {
const pages = getCurrentPages()
return pages[pages.length - 1]
}
/**
* @description 判断target是否对象
* @param obj
* @return {boolean}
*/
export function isObj(obj) {
// Object.prototype.toString.call(obj).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object'
return typeof obj === 'object'
}
/**
* @description 获取目标原始类型
* @param target 任意类型
* @returns {string} type 数据类型
*/
export function getType(target) {
// 得到原生类型
const typeStr = Object.prototype.toString.call(target)
// 拿到类型值
const type = typeStr.match(/\[object (\w+)\]/)[1]
// 类型值转小写并返回
return type.toLowerCase()
}
/**
* @description 默认的外部格式化函数 - picker组件
* @param items
* @param labelKey
* @return {*}
*/
export const defaultDisplayFormat = function (items, { labelKey = 'value' }) {
// 在props中this被指向了全局data
if (items instanceof Array) {
return items.map((item) => item[labelKey]).toString()
} else {
return items[labelKey]
}
}
/**
* @description 默认函数占位符 - pickerView组件
* @param value
* @return value
*/
export const defaultFunction = (value) => value
/**
* @description 是否不为空
* @param value
* @return {Boolean}
*/
export const isDef = (value) => value !== undefined && value !== null
export { debounce }
/**
* @description 防止数字小于零
* @param {Number} num
* @param {String} label 标签
*/
export const checkNumRange = (num, label = 'value') => {
if (num < 0) {
throw Error(`${label} shouldn't be less than zero`)
}
}
/**
* @description 防止pixel无意义
* @param {Number} num
* @param {String} label 标签
*/
export const checkPixelRange = (num, label = 'value') => {
if (num <= 0) {
throw Error(`${label} should be greater than zero`)
}
}
/**
* @default 渲染视图
* @param {this} node 节点
* @param {Object} data 需要渲染的数据
* @param {Number} delay 延迟多久
*/
export const renderData = (node, data, delay = 0) => {
const diff = Object.keys(data).reduce((prev, key) => {
if (data[key] !== node.data[key]) {
prev[key] = data[key]
}
return prev
}, {})
if (Object.keys(diff).length === 0) return
const render = () => node.setData(diff)
delay ? setTimeout(render, delay) : render()
}
function rgbToHex(r, g, b) {
const hex = ((r << 16) | (g << 8) | b).toString(16)
return '#' + new Array(Math.abs(hex.length - 7)).join('0') + hex
}
function hexToRgb(hex) {
const rgb = []
for (let i = 1; i < 7; i += 2) {
rgb.push(parseInt('0x' + hex.slice(i, i + 2)))
}
return rgb
}
/**
* @default 计算渐变色的中间变量
* @param {String} startColor 开始颜色
* @param {String} endColor 结束颜色
* @param {Number} step 获取渲染位置默认为中间位置
* @return {String} 渐变色中间颜色变量
*/
export const gradient = (startColor, endColor, step = 2) => {
// 将hex转换为rgb
const sColor = hexToRgb(startColor)
const eColor = hexToRgb(endColor)
// 计算R\G\B每一步的差值
const rStep = (eColor[0] - sColor[0]) / step
const gStep = (eColor[1] - sColor[1]) / step
const bStep = (eColor[2] - sColor[2]) / step
const gradientColorArr = []
for (let i = 0; i < step; i++) {
// 计算每一步的hex值
gradientColorArr.push(rgbToHex(parseInt(rStep * i + sColor[0]), parseInt(gStep * i + sColor[1]), parseInt(bStep * i + sColor[2])))
}
return gradientColorArr
}
/** @description 保证num不超出min和max的范围 */
export const range = (num, min, max) => Math.min(Math.max(num, min), max)
/** @description 比较数值是否相等 */
export const isEqual = (value1, value2) => {
if (value1 === value2) return true
if (!(value1 instanceof Array)) return false
if (!(value2 instanceof Array)) return false
if (value1.length !== value2.length) return false
for (let i = 0; i !== value1.length; ++i) {
if (value1[i] !== value2[i]) return false
}
return true
}
/** @description 不满10补0 */
export const padZero = (number, length = 2) => {
number = number + ''
while (number.length < length) {
number = '0' + number
}
return number
}
/** @description 全局变量id */
export const context = {
id: 1000
}

View File

@ -0,0 +1,43 @@
export default {
methods: {
$emit(...args) {
this.triggerEvent(...args)
},
/**
* @description 获取节点的样式
* @param {String} selector -选择器
* @param {Boolean} all -selectAll
* @return {Promise<Object | Array<Object>>} 样式对象或者所有节点样式的集合
*/
getRect(selector, all = false) {
return new Promise((resolve) => {
jd.createSelectorQuery()
.in(this)
[all ? 'selectAll' : 'select'](selector)
.boundingClientRect((rect) => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect)
}
if (!all && rect) {
resolve(rect)
}
})
.exec()
})
},
/**
* @default 模拟 requestAnimationFrame支持 Promise 嵌套
* @param {Function} cb 下一渲染帧的回调
*/
requestAnimationFrame(cb = () => void 0) {
return new Promise((resolve, reject) => {
if (typeof cb !== 'function' || !this || !('setData' in this)) return reject
this.setData({}, () => {
resolve()
cb()
})
})
}
}
}

View File

@ -0,0 +1,24 @@
export default Behavior({
properties: {
border: Boolean
},
methods: {
/**
* @description 从cellGroup获取此组件的索引
* @return {Number} 此组件的索引
*/
getIndex() {
if (!this.parent) return
return this.parent.children.indexOf(this)
},
/**
* @description 为所有索引非0的组件设置刘海线此方法由cellGroup调用
*/
setIndexAndStatus(border) {
const index = this.getIndex()
this.setData({
border: border && index
})
}
}
})

View File

@ -0,0 +1,237 @@
import { pushToQueue, removeFromQueue, closeOther } from '../common/clickoutside'
/**
* @description 注意点
* 1. 需要控制的位置 12
* 2. 每一个位置改变都需要控制
* popLeft(弹出坐标x)/ popTop(弹出坐标Y)/ arrowStyle(三角形位置以及尖角朝向)
* 尖角样式朝向class控制位置用js控制
* @param {String} [placement=bottom] - Placement of the popper accepted values: top(-start, -end), right(-start, -end), bottom(-start, -end), left(-start, -end)
* @param {Number} [offset=5] - Amount of pixels the popper will be shifted (can be negative).
* @param {Boolean} [visibleArrow=false] Visibility of the arrow
* @param {Boolean} [value=false] Visibility of the component
* @param {Boolean} [disabled=false] Disabled to change.
*/
export default function () {
return {
externalClasses: ['custom-arrow', 'custom-pop'],
data: {
popStyle: {},
arrowStyle: {},
showStyle: ''
},
props: {
visibleArrow: {
type: Boolean,
value: true
},
// 显示内容 String || Array
content: {
type: null,
observer(newVal) {
const { mode, selector } = this.data
// 类型校验,支持所有值(除null、undefined。undefined建议统一写成void (0)防止全局undefined被覆盖)
if (newVal === null || newVal === undefined) {
throw Error("value can't be null or undefined")
}
if (selector === 'popover' && mode === 'normal' && typeof newVal !== 'string') {
throw Error('The value type must be a string type in normal mode')
} else if (selector === 'popover' && mode === 'menu' && this.checkType(newVal) !== 'Array') {
throw Error('The value type must be a Array type in menu mode')
}
}
},
placement: {
type: String,
value: 'bottom'
},
offset: {
type: Number,
value: 0
},
useContentSlot: {
type: Boolean,
value: false
},
disabled: {
type: Boolean,
value: false
},
showClose: {
type: Boolean,
value: false
},
show: {
type: Boolean,
observer(newValue, oldValue) {
if (newValue) {
this.control()
closeOther(this)
}
this.setData({ showStyle: newValue ? 'display: inline-block;' : 'display: none;' })
this.$emit('change', { show: newValue })
this.$emit(`${newValue ? 'open' : 'close'}`)
}
}
},
mounted() {
this.init()
},
beforeCreate() {
pushToQueue(this)
},
created() {
this.setData({ showStyle: this.data.show ? 'opacity: 1;' : 'opacity: 0;' })
},
destroyed() {
removeFromQueue(this)
},
methods: {
noop() {},
open() {
this.setData({ show: true })
},
close() {
this.setData({ show: false })
},
init() {
// 初始化 class
const { placement, visibleArrow, selector } = this.data
if (visibleArrow) {
let arrowClass = [
`wd-${selector}__arrow`,
placement === 'bottom' || placement === 'bottom-start' || placement === 'bottom-end' ? `wd-${selector}__arrow-up` : '',
placement === 'left' || placement === 'left-start' || placement === 'left-end' ? `wd-${selector}__arrow-right` : '',
placement === 'right' || placement === 'right-start' || placement === 'right-end' ? `wd-${selector}__arrow-left` : '',
placement === 'top' || placement === 'top-start' || placement === 'top-end' ? `wd-${selector}__arrow-down` : ''
]
arrowClass = arrowClass.join(' ')
this.setData({ arrowClass })
}
// 初始化数据获取
this.getRect('#target').then((rect) => {
if (!rect) return
this.left = rect.left
this.bottom = rect.bottom
this.width = rect.width
this.height = rect.height
this.top = rect.top
})
// 用透明度可在初始化时获取到pop尺寸
this.getRect('#pos').then((rect) => {
if (!rect) return
this.popWidth = rect.width
this.popHeight = rect.height
})
},
toggle(event) {
if (this.data.disabled) return
const { show } = this.data
this.setData({ show: !show })
},
checkType(value) {
return Object.prototype.toString.call(value).slice(8, -1)
},
control() {
const { placement, offset } = this.data
// arrow size
const arrowSize = 9
// 上下位(纵轴)对应的距离左边的距离
const verticalX = this.width / 2
// 上下位(纵轴)对应的距离底部的距离
const verticalY = arrowSize + this.height + 5
// 左右位(横轴)对应的距离左边的距离
const horizontalX = this.width + arrowSize + 5
// 左右位(横轴)对应的距离底部的距离
const horizontalY = this.height / 2
const offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset
const offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + offset
const placements = new Map([
// 上
['top', [`left: ${verticalX}px; bottom: ${verticalY}px; transform: translateX(-50%);`, 'left: 50%;']],
[
'top-start',
[
`left: ${offsetX}px; bottom: ${verticalY}px;`,
`left: ${(this.popWidth >= this.width ? this.width / 2 : this.popWidth - 25) - offsetX}px;`
]
],
[
'top-end',
[
`right: ${offsetX}px; bottom: ${verticalY}px;`,
`right: ${(this.popWidth >= this.width ? this.width / 2 : this.popWidth - 25) - offsetX}px; transform: translateX(50%);`
]
],
// 下
['bottom', [`left: ${verticalX}px; top: ${verticalY}px; transform: translateX(-50%);`, 'left: 50%;']],
[
'bottom-start',
[`left: ${offsetX}px; top: ${verticalY}px;`, `left: ${(this.popWidth >= this.width ? this.width / 2 : this.popWidth - 25) - offsetX}px;`]
],
[
'bottom-end',
[
`right: ${offsetX}px; top: ${verticalY}px;`,
`right: ${(this.popWidth >= this.width ? this.width / 2 : this.popWidth - 25) - offsetX}px; transform: translateX(50%);`
]
],
// 左
['left', [`right: ${horizontalX}px; top: ${horizontalY}px; transform: translateY(-50%);`, 'top: 50%']],
[
'left-start',
[
`right: ${horizontalX}px; top: ${offsetY}px;`,
`top: ${(this.popHeight >= this.height ? this.height / 2 : this.popHeight - 20) - offsetY}px;`
]
],
[
'left-end',
[
`right: ${horizontalX}px; bottom: ${offsetY}px;`,
`bottom: ${(this.popHeight >= this.height ? this.height / 2 : this.popHeight - 20) - offsetY}px; transform: translateY(50%);`
]
],
// 右
['right', [`left: ${horizontalX}px; top: ${horizontalY}px; transform: translateY(-50%);`, 'top: 50%']],
[
'right-start',
[
`left: ${horizontalX}px; top: ${offsetY}px;`,
`top: ${(this.popHeight >= this.height ? this.height / 2 : this.popHeight - 20) - offsetY}px;`
]
],
[
'right-end',
[
`left: ${horizontalX}px; bottom: ${offsetY}px;`,
`bottom: ${(this.popHeight >= this.height ? this.height / 2 : this.popHeight - 20) - offsetY}px; transform: translateY(50%);`
]
]
])
this.setData({
popStyle: placements.get(placement)[0],
arrowStyle: placements.get(placement)[1]
})
}
}
}
}

View File

@ -0,0 +1,25 @@
export default function () {
return {
methods: {
touchStart(event) {
const touch = event.touches[0]
this.direction = ''
this.deltaX = 0
this.deltaY = 0
this.offsetX = 0
this.offsetY = 0
this.startX = touch.clientX
this.startY = touch.clientY
},
touchMove(event) {
const touch = event.touches[0]
this.deltaX = touch.clientX - this.startX
this.deltaY = touch.clientY - this.startY
this.offsetX = Math.abs(this.deltaX)
this.offsetY = Math.abs(this.deltaY)
this.direction = this.offsetX > this.offsetY ? 'horizontal' : this.offsetX < this.offsetY ? 'vertical' : ''
}
}
}
}

View File

@ -0,0 +1,160 @@
import { onBeforeMount, ref, watch } from 'vue'
import { isObj } from '../common/util'
const getClassNames = (name) => {
if (!name) {
return {
enter: 'enter-class enter-active-class',
'enter-to': 'enter-to-class enter-active-class',
leave: 'leave-class leave-active-class',
'leave-to': 'leave-to-class leave-active-class'
}
}
return {
enter: `wd-${name}-enter wd-${name}-enter-active`,
'enter-to': `wd-${name}-enter-to wd-${name}-enter-active`,
leave: `wd-${name}-leave wd-${name}-leave-active`,
'leave-to': `wd-${name}-leave-to wd-${name}-leave-active`
}
}
const requestAnimationFrame = (cb = () => void 0) => {
return new Promise((resolve, reject) => {
uni
.createSelectorQuery()
.selectViewport()
.boundingClientRect()
.exec(() => {
resolve(true)
cb()
})
})
}
type TransitionName =
| 'fade'
| 'fade-down'
| 'fade-left'
| 'fade-right'
| 'fade-up'
| 'slide-down'
| 'slide-left'
| 'slide-right'
| 'slide-up'
| 'zoom-in'
| 'zoom-out'
interface Props {
show: boolean
duration?: Record<string, number> | number
name: TransitionName
customStyle: string
lazyRender: boolean
// 定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
enterClass?: string
// 定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
enterActiveClass?: string
// 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 enter-class 被移除),在过渡/动画完成之后移除。
enterToClass?: string
// 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
leaveClass?: string
// 定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
leaveActiveClass?: string
// 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 leave-class 被删除),在过渡/动画完成之后移除。
leaveToClass?: string
}
const props = withDefaults(defineProps<Props>(), {
show: false,
name: 'fade',
duration: 300,
lazyRender: true
})
// 初始化是否完成
const inited = ref<boolean>(false)
// 是否显示
const display = ref<boolean>(false)
// 当前动画状态
const status = ref<string>('')
// 动画是否结束
const transitionEnded = ref<boolean>(false)
// 动画持续时间
const currentDuration = ref<number>(300)
// 类名
const classes = ref<string>('')
const emit = defineEmits(['click', 'before-enter', 'enter', 'before-leave', 'leave', 'after-leave', 'after-enter'])
onBeforeMount(() => {
if (props.show) {
enter()
}
})
watch(
() => props.show,
(newVal) => {
observerShow(newVal)
}
)
function observerShow(value: boolean) {
value ? enter() : leave()
}
function enter() {
const classNames = getClassNames(props.name)
const duration = isObj(props.duration) ? (props.duration as any).enter : props.duration
status.value = 'enter'
emit('before-enter')
requestAnimationFrame(() => {
emit('enter')
classes.value = classNames.enter
currentDuration.value = duration
requestAnimationFrame(() => {
inited.value = true
display.value = true
requestAnimationFrame(() => {
transitionEnded.value = false
classes.value = classNames['enter-to']
})
})
})
}
function leave() {
if (!display.value) return
const classNames = getClassNames(props.name)
const duration = isObj(props.duration) ? (props.duration as any).leave : props.duration
status.value = 'leave'
emit('before-leave')
requestAnimationFrame(() => {
emit('leave')
classes.value = classNames.leave
currentDuration.value = duration
requestAnimationFrame(() => {
transitionEnded.value = false
setTimeout(() => onTransitionEnd(), currentDuration.value)
classes.value = classNames['leave-to']
})
})
}
function onTransitionEnd() {
if (transitionEnded.value) return
transitionEnded.value = true
if (status.value === 'leave') {
// 离开后触发
emit('after-leave')
} else if (status.value === 'enter') {
// 进入后触发
emit('after-enter')
}
if (!props.show && display.value) {
display.value = false
}
}

View File

@ -0,0 +1,101 @@
import VueComponent from '../common/component'
VueComponent({
externalClasses: ['custom-header-class'],
props: {
show: Boolean,
actions: {
type: Array,
value: []
},
panels: {
type: Array,
value: [],
observer: 'computedValue'
},
title: String,
cancelText: String,
closeOnClickAction: {
type: Boolean,
value: true
},
closeOnClickModal: {
type: Boolean,
value: true
},
duration: {
type: Number,
value: 200
},
zIndex: {
type: Number,
value: 10
},
lazyRender: {
type: Boolean,
value: true
},
safeAreaInsetBottom: {
type: Boolean,
value: true
}
},
data() {
return {
formatPanels: []
}
},
methods: {
isArray() {
return this.data.panels.length && !(this.data.panels[0] instanceof Array)
},
computedValue() {
this.setData({
formatPanels: this.isArray() ? [this.data.panels] : this.data.panels
})
},
select(event) {
const { rowIndex, colIndex, type } = event.currentTarget.dataset
if (type === 'action') {
this.$emit('select', {
item: this.data.actions[rowIndex],
index: rowIndex
})
} else if (this.isArray()) {
this.$emit('select', {
item: this.data.panels[colIndex],
index: colIndex
})
} else {
this.$emit('select', {
item: this.data.panels[rowIndex][colIndex],
rowIndex,
colIndex
})
}
this.close()
},
handleClickModal() {
this.$emit('clickmodal')
if (this.data.closeOnClickModal) {
this.close()
}
},
handleCancel() {
this.$emit('cancel')
this.close()
},
close() {
this.$emit('close')
},
handleOpen() {
this.$emit('open')
},
handleOpened() {
this.$emit('opened')
},
handleClosed() {
this.$emit('closed')
}
}
})

View File

@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"wd-icon": "../icon/index",
"wd-loading": "../loading/index",
"wd-popup": "../popup/index"
}
}

View File

@ -0,0 +1,67 @@
<wd-popup
custom-class="wd-action-sheet__popup"
custom-style="{{ (actions && actions.length || panels && panels.length) ? 'background: transparent;' : '' }}"
show="{{ show }}"
duration="{{ duration }}"
position="bottom"
close-on-click-modal="{{ closeOnClickModal }}"
safe-area-inset-bottom="{{ safeAreaInsetBottom }}"
lazy-render="{{ lazyRender }}"
bind:enter="handleOpen"
bind:close="close"
bind:afterenter="handleOpened"
bind:afterleave="handleClosed"
z-index="{{ zIndex }}"
>
<view class="wd-action-sheet" style="{{ (actions && actions.length || panels && panels.length) ? 'margin: 0 10px 10px; border-radius: 16px;' : '' }}">
<view v-if="{{ title }}" class="wd-action-sheet__header custom-header-class">
{{ title }}
<wd-icon custom-class="wd-action-sheet__close" name="add" bind:tap="close"/>
</view>
<view class="wd-action-sheet__actions" v-if="{{ actions && actions.length }}">
<button
jd:for="{{ actions }}"
jd:key="rowIndex"
class="wd-action-sheet__action {{ item.disabled ? 'wd-action-sheet__action--disabled' : '' }} {{ item.loading ? 'wd-action-sheet__action--loading' : '' }}"
style="color: {{ item.color }}"
jd:for-index="rowIndex"
data-row-index="{{ rowIndex }}"
data-type = "action"
bind:tap="select"
>
<wd-loading v-if="{{ item.loading }}" size="20px"/>
<view v-else class="wd-action-sheet__name">{{ item.name }}</view>
<view v-if="{{ !item.loading && item.subname }}" class="wd-action-sheet__subname">{{ item.subname }}</view>
</button>
</view>
<view v-if="{{ formatPanels && formatPanels.length }}">
<view
jd:for="{{ formatPanels }}"
data-index="{{ rowIndex }}"
jd:for-item="item"
jd:for-index="rowIndex"
jd:key="rowIndex"
class="wd-action-sheet__panels"
>
<view class="wd-action-sheet__panels-content">
<view
jd:for="{{ item }}"
data-col-index="{{ colIndex }}"
data-row-index="{{ rowIndex }}"
data-type = "panels"
jd:for-item="panel"
jd:for-index="colIndex"
jd:key="colIndex"
class="wd-action-sheet__panel"
bind:tap="select"
>
<image class="wd-action-sheet__panel-img" src="{{ panel.iconUrl }}" />
<view class="wd-action-sheet__panel-title">{{ panel.title }}</view>
</view>
</view>
</view>
</view>
<slot />
<button v-if="{{ cancelText }}" class="wd-action-sheet__cancel" bind:tap="handleCancel">{{ cancelText }}</button>
</view>
</wd-popup>

View File

@ -0,0 +1,147 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
@include b(action-sheet) {
background-color: #fff;
padding-bottom: 1px;
@include e(popup) {
border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
}
@include e(actions) {
padding: 8px 0;
max-height: 50vh;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
@include e(action) {
position: relative;
display: block;
width: 100%;
height: $-action-sheet-action-height;
line-height: $-action-sheet-action-height;
color: $-action-sheet-color;
font-size: $-action-sheet-fs;
text-align: center;
border: none;
background: $-action-sheet-bg;
outline: none;
&:after {
display: none;
}
&:active {
background: $-action-sheet-active-color;
}
@include m(disabled) {
color: $-action-sheet-disabled-color;
}
@include m(loading) {
display: flex;
align-items: center;
justify-content: center;
line-height: initial;
}
}
@include e(name) {
display: inline-block;
}
@include e(subname) {
display: inline-block;
margin-left: 4px;
font-size: $-action-sheet-subname-fs;
color: $-action-sheet-subname-color;
}
@include e(cancel) {
display: block;
width: calc(100% - 48px);
line-height: $-action-sheet-cancel-height;
padding: 0;
color: $-action-sheet-cancel-color;
font-size: $-action-sheet-fs;
text-align: center;
border-radius: $-action-sheet-cancel-radius;
border: none;
background: $-action-sheet-cancel-bg;
outline: none;
margin: 0 auto 24px;
font-weight: $-action-sheet-weight;
&:active {
background: $-action-sheet-active-color;
}
&:after {
display: none;
}
}
@include e(header) {
color: $-action-sheet-color;
position: relative;
height: $-action-sheet-title-height;
line-height: $-action-sheet-title-height;
text-align: center;
font-size: $-action-sheet-title-fs;
font-weight: $-action-sheet-weight;
}
@include e(close) {
position: absolute;
top: $-action-sheet-close-top;
right: $-action-sheet-close-right;
color: $-action-sheet-close-color;
font-size: $-action-sheet-close-fs;
transform: rotate(-45deg);
line-height: 1.1;
}
@include e(panels) {
height: 84px;
overflow-y: hidden;
&:first-of-type {
margin-top: 20px;
}
&:last-of-type {
margin-bottom: 12px;
}
}
@include e(panels-content) {
display: flex;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@include e(panel) {
width: 88px;
flex: 0 0 auto;
display: inline-block;
padding: $-action-sheet-panel-padding;
}
@include e(panel-img) {
display: block;
width: $-action-sheet-panel-img-fs;
height: $-action-sheet-panel-img-fs;
margin: 0 auto;
margin-bottom: 7px;
border-radius: $-action-sheet-panel-img-radius;
}
@include e(panel-title) {
font-size: $-action-sheet-subname-fs;
line-height: 1.2;
text-align: center;
color: $-action-sheet-color;
@include lineEllipsis;
}
}

View File

@ -0,0 +1,323 @@
<template>
<wd-popup
custom-class="wd-action-sheet__popup"
custom-style="{{ (actions && actions.length || panels && panels.length) ? 'background: transparent;' : '' }}"
show="{{ show }}"
duration="{{ duration }}"
position="bottom"
close-on-click-modal="{{ closeOnClickModal }}"
safe-area-inset-bottom="{{ safeAreaInsetBottom }}"
lazy-render="{{ lazyRender }}"
bind:enter="handleOpen"
bind:close="close"
bind:afterenter="handleOpened"
bind:afterleave="handleClosed"
z-index="{{ zIndex }}"
>
<view
class="wd-action-sheet"
style="{{ (actions && actions.length || panels && panels.length) ? 'margin: 0 10px 10px; border-radius: 16px;' : '' }}"
>
<view v-if="{{ title }}" class="wd-action-sheet__header custom-header-class">
{{ title }}
<wd-icon custom-class="wd-action-sheet__close" name="add" bind:tap="close" />
</view>
<view class="wd-action-sheet__actions" v-if="{{ actions && actions.length }}">
<button
jd:for="{{ actions }}"
jd:key="rowIndex"
class="wd-action-sheet__action {{ item.disabled ? 'wd-action-sheet__action--disabled' : '' }} {{ item.loading ? 'wd-action-sheet__action--loading' : '' }}"
style="color: {{ item.color }}"
jd:for-index="rowIndex"
data-row-index="{{ rowIndex }}"
data-type="action"
bind:tap="select"
>
<wd-loading v-if="{{ item.loading }}" size="20px" />
<view v-else class="wd-action-sheet__name">{{ item.name }}</view>
<view v-if="{{ !item.loading && item.subname }}" class="wd-action-sheet__subname">{{ item.subname }}</view>
</button>
</view>
<view v-if="{{ formatPanels && formatPanels.length }}">
<view
jd:for="{{ formatPanels }}"
data-index="{{ rowIndex }}"
jd:for-item="item"
jd:for-index="rowIndex"
jd:key="rowIndex"
class="wd-action-sheet__panels"
>
<view class="wd-action-sheet__panels-content">
<view
jd:for="{{ item }}"
data-col-index="{{ colIndex }}"
data-row-index="{{ rowIndex }}"
data-type="panels"
jd:for-item="panel"
jd:for-index="colIndex"
jd:key="colIndex"
class="wd-action-sheet__panel"
bind:tap="select"
>
<image class="wd-action-sheet__panel-img" src="{{ panel.iconUrl }}" />
<view class="wd-action-sheet__panel-title">{{ panel.title }}</view>
</view>
</view>
</view>
</view>
<slot />
<button v-if="{{ cancelText }}" class="wd-action-sheet__cancel" bind:tap="handleCancel">{{ cancelText }}</button>
</view>
</wd-popup>
</template>
<script>
export default {
props: {
show: Boolean,
actions: {
type: Array,
value: []
},
panels: {
type: Array,
value: [],
observer: 'computedValue'
},
title: String,
cancelText: String,
closeOnClickAction: {
type: Boolean,
value: true
},
closeOnClickModal: {
type: Boolean,
value: true
},
duration: {
type: Number,
value: 200
},
zIndex: {
type: Number,
value: 10
},
lazyRender: {
type: Boolean,
value: true
},
safeAreaInsetBottom: {
type: Boolean,
value: true
}
},
data() {
return {
formatPanels: []
}
},
methods: {
isArray() {
return this.data.panels.length && !(this.data.panels[0] instanceof Array)
},
computedValue() {
this.setData({
formatPanels: this.isArray() ? [this.data.panels] : this.data.panels
})
},
select(event) {
const { rowIndex, colIndex, type } = event.currentTarget.dataset
if (type === 'action') {
this.$emit('select', {
item: this.data.actions[rowIndex],
index: rowIndex
})
} else if (this.isArray()) {
this.$emit('select', {
item: this.data.panels[colIndex],
index: colIndex
})
} else {
this.$emit('select', {
item: this.data.panels[rowIndex][colIndex],
rowIndex,
colIndex
})
}
this.close()
},
handleClickModal() {
this.$emit('clickmodal')
if (this.data.closeOnClickModal) {
this.close()
}
},
handleCancel() {
this.$emit('cancel')
this.close()
},
close() {
this.$emit('close')
},
handleOpen() {
this.$emit('open')
},
handleOpened() {
this.$emit('opened')
},
handleClosed() {
this.$emit('closed')
}
}
}
</script>
<style lang="scss" scoped>
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
@include b(action-sheet) {
background-color: #fff;
padding-bottom: 1px;
@include e(popup) {
border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
}
@include e(actions) {
padding: 8px 0;
max-height: 50vh;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
@include e(action) {
position: relative;
display: block;
width: 100%;
height: $-action-sheet-action-height;
line-height: $-action-sheet-action-height;
color: $-action-sheet-color;
font-size: $-action-sheet-fs;
text-align: center;
border: none;
background: $-action-sheet-bg;
outline: none;
&:after {
display: none;
}
&:active {
background: $-action-sheet-active-color;
}
@include m(disabled) {
color: $-action-sheet-disabled-color;
}
@include m(loading) {
display: flex;
align-items: center;
justify-content: center;
line-height: initial;
}
}
@include e(name) {
display: inline-block;
}
@include e(subname) {
display: inline-block;
margin-left: 4px;
font-size: $-action-sheet-subname-fs;
color: $-action-sheet-subname-color;
}
@include e(cancel) {
display: block;
width: calc(100% - 48px);
line-height: $-action-sheet-cancel-height;
padding: 0;
color: $-action-sheet-cancel-color;
font-size: $-action-sheet-fs;
text-align: center;
border-radius: $-action-sheet-cancel-radius;
border: none;
background: $-action-sheet-cancel-bg;
outline: none;
margin: 0 auto 24px;
font-weight: $-action-sheet-weight;
&:active {
background: $-action-sheet-active-color;
}
&:after {
display: none;
}
}
@include e(header) {
color: $-action-sheet-color;
position: relative;
height: $-action-sheet-title-height;
line-height: $-action-sheet-title-height;
text-align: center;
font-size: $-action-sheet-title-fs;
font-weight: $-action-sheet-weight;
}
@include e(close) {
position: absolute;
top: $-action-sheet-close-top;
right: $-action-sheet-close-right;
color: $-action-sheet-close-color;
font-size: $-action-sheet-close-fs;
transform: rotate(-45deg);
line-height: 1.1;
}
@include e(panels) {
height: 84px;
overflow-y: hidden;
&:first-of-type {
margin-top: 20px;
}
&:last-of-type {
margin-bottom: 12px;
}
}
@include e(panels-content) {
display: flex;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@include e(panel) {
width: 88px;
flex: 0 0 auto;
display: inline-block;
padding: $-action-sheet-panel-padding;
}
@include e(panel-img) {
display: block;
width: $-action-sheet-panel-img-fs;
height: $-action-sheet-panel-img-fs;
margin: 0 auto;
margin-bottom: 7px;
border-radius: $-action-sheet-panel-img-radius;
}
@include e(panel-title) {
font-size: $-action-sheet-subname-fs;
line-height: 1.2;
text-align: center;
color: $-action-sheet-color;
@include lineEllipsis;
}
}
</style>

View File

@ -0,0 +1,39 @@
import VueComponent from '../common/component'
VueComponent({
props: {
value: {
type: String,
value: null,
observer: 'notice'
},
bgColor: String,
max: {
type: Number,
observer: 'notice'
},
isDot: {
type: Boolean,
value: false,
observer: 'notice'
},
hidden: Boolean,
type: String,
top: Number,
right: Number
},
data: {
content: ''
},
methods: {
notice () {
if (this.data.isDot) return
let value = this.data.value
const max = this.data.max
if (value && max && !Number.isNaN(value) && !Number.isNaN(max)) {
value = max < value ? `${parseInt(max)}+` : value
}
this.setData({ content: value })
}
}
})

View File

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"wd-icon": "../icon/index",
"wd-button": "../button/index"
}
}

View File

@ -0,0 +1,9 @@
<div class="wd-badge custom-class">
<slot></slot>
<view
v-if="{{!hidden && (content || content === 0 || isDot)}}"
class="wd-badge__content is-fixed {{type ? 'wd-badge__content--' + type : ''}} {{isDot?'is-dot':'' }}"
style="background-color: {{bgColor}}; top: {{top || 0}}px; right: {{right || 0}}px">
{{content}}
</view>
</div>

View File

@ -0,0 +1,51 @@
@import "./../common/abstracts/_mixin.scss";
@import "./../common/abstracts/variable.scss";
@include b(badge) {
position: relative;
vertical-align: middle;
display: inline-block;
@include e(content) {
display: inline-block;
height: $-badge-height;
line-height: $-badge-height;
padding: $-badge-padding;
background-color: $-badge-bg;
border-radius: calc($-badge-height / 2 + 2px);
color: $-badge-color;
font-size: $-badge-fs;
text-align: center;
white-space: nowrap;
border: $-badge-border;
font-weight: 500;
@include when(fixed) {
position: absolute;
transform: translateY(-50%) translateX(50%);
}
@include when(dot) {
height: $-badge-dot-size;
width: $-badge-dot-size;
padding: 0;
border-radius: 50%;
}
@each $type in (primary, success, warning, info, danger) {
@include m($type) {
@if $type == primary {
background-color: $-badge-primary;
} @else if $type == success {
background-color: $-badge-success;
} @else if $type == warning {
background-color: $-badge-warning;
} @else if $type == info {
background-color: $-badge-info;
} @else {
background-color: $-badge-danger;
}
}
}
}
}

View File

@ -0,0 +1,108 @@
<template>
<view class="wd-badge custom-class">
<slot></slot>
<view
v-if="!hidden && (content || content === 0 || isDot)"
:class="['wd-badge__content', 'is-fixed', type ? 'wd-badge__content--' + type : '', isDot ? 'is-dot' : '']"
:style="contentStyle"
>
{{ content }}
</view>
</view>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
interface Props {
modelValue: number | null
bgColor?: string
max?: number
isDot?: boolean
hidden?: boolean
type?: string
top?: number
right?: number
}
const props = withDefaults(defineProps<Props>(), {
modelValue: null
})
const content = ref<number | null>(null)
watch(
[() => props.modelValue, () => props.max, () => props.isDot],
() => {
notice()
},
{ immediate: true, deep: true }
)
const contentStyle = computed(() => {
return `'background-color': ${props.bgColor};top:${props.top || 0}px; right: ${props.right || 0} px`
})
function notice() {
if (props.isDot) return
let value = props.modelValue
const max = props.max
if (value && max && !Number.isNaN(value) && !Number.isNaN(max)) {
value = max < value ? max : value
}
content.value = value
}
</script>
<script></script>
<style lang="scss" scoped>
@import './../common/abstracts/_mixin.scss';
@import './../common/abstracts/variable.scss';
@include b(badge) {
position: relative;
vertical-align: middle;
display: inline-block;
@include e(content) {
display: inline-block;
height: $-badge-height;
line-height: $-badge-height;
padding: $-badge-padding;
background-color: $-badge-bg;
border-radius: calc($-badge-height / 2 + 2px);
color: $-badge-color;
font-size: $-badge-fs;
text-align: center;
white-space: nowrap;
border: $-badge-border;
font-weight: 500;
@include when(fixed) {
position: absolute;
transform: translateY(-50%) translateX(50%);
}
@include when(dot) {
height: $-badge-dot-size;
width: $-badge-dot-size;
padding: 0;
border-radius: 50%;
}
@each $type in (primary, success, warning, info, danger) {
@include m($type) {
@if $type == primary {
background-color: $-badge-primary;
} @else if $type == success {
background-color: $-badge-success;
} @else if $type == warning {
background-color: $-badge-warning;
} @else if $type == info {
background-color: $-badge-info;
} @else {
background-color: $-badge-danger;
}
}
}
}
}
</style>

View File

@ -0,0 +1,119 @@
import VueComponent from '../common/component'
import base64 from '../common/base64'
const loadingIcon = (color = '#4D80F0', reverse = true) => {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 42"><defs><linearGradient x1="100%" y1="0%" x2="0%" y2="0%" id="a"><stop stop-color="${reverse ? color : '#fff'}" offset="0%" stop-opacity="0"/><stop stop-color="${reverse ? color : '#fff'}" offset="100%"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><path d="M21 1c11.046 0 20 8.954 20 20s-8.954 20-20 20S1 32.046 1 21 9.954 1 21 1zm0 7C13.82 8 8 13.82 8 21s5.82 13 13 13 13-5.82 13-13S28.18 8 21 8z" fill="${reverse ? '#fff' : color}"/><path d="M4.599 21c0 9.044 7.332 16.376 16.376 16.376 9.045 0 16.376-7.332 16.376-16.376" stroke="url(#a)" stroke-width="3.5" stroke-linecap="round"/></g></svg>`
}
VueComponent({
props: {
plain: Boolean,
disabled: Boolean,
round: {
type: Boolean,
value: true
},
suck: Boolean,
block: Boolean,
type: {
type: String,
value: 'primary'
},
size: {
type: String,
value: 'medium'
},
icon: String,
loading: {
type: Boolean,
observer: 'buildLoadingSvg'
},
loadingColor: String,
openType: String,
formType: String,
hoverStopPropagation: {
type: Boolean,
value: false
},
lang: {
type: String,
value: 'en'
},
sessionFrom: String,
sendMessageTitle: String,
sendMessagePath: String,
sendMessageImg: String,
appParameter: String,
showMessageCard: {
type: Boolean,
value: false
}
},
data: {
hoverStartTime: 20,
hoverStayTime: 70,
loadingIconSvg: ''
},
methods: {
handleClick (event) {
if (!this.data.disabled && !this.data.loading) {
this.$emit('click', event.detail)
}
},
handleGetuserinfo (event) {
this.$emit('getuserinfo', event.detail)
},
handleConcat (event) {
this.$emit('contact', event.detail)
},
handleGetphonenumber (event) {
this.$emit('getphonenumber', event.detail)
},
handleError (event) {
this.$emit('error', event.detail)
},
handleLaunchapp (event) {
this.$emit('launchapp', event.detail)
},
handleOpensetting (event) {
this.$emit('opensetting', event.detail)
},
buildLoadingSvg () {
const { loadingColor, type, plain } = this.data
let color = loadingColor
if (!color) {
switch (type) {
case 'primary':
color = '#4D80F0'
break
case 'success':
color = '#34d19d'
break
case 'info':
color = '#333'
break
case 'warning':
color = '#f0883a'
break
case 'error':
color = '#fa4350'
break
case 'default':
color = '#333'
break
}
}
const svg = loadingIcon(color, !plain)
const svgStr = `"data:image/svg+xml;base64,${base64(svg)}"`
this.setData({ loadingIconSvg: svgStr })
}
}
})

View File

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"wd-icon": "../icon/index",
"wd-loading": "../loading/index"
}
}

View File

@ -0,0 +1,30 @@
<button
hover-class="wd-button--active"
class="wd-button custom-class is-{{type}} is-{{size}} {{ plain ? 'is-plain' : '' }} {{ disabled ? 'is-disabled' : '' }} {{ round ? 'is-round' : '' }} {{ suck ? 'is-suck' : '' }} {{ block ? 'is-block' : '' }} {{ loading ? 'is-loading' : '' }}"
open-type="{{ openType }}"
send-message-title="{{sendMessageTitle}}"
send-message-path="{{sendMessagePath}}"
send-message-img="{{sendMessageImg}}"
app-parameter="{{appParameter}}"
show-message-card="{{showMessageCard}}"
session-from="{{sessionFrom}}"
session-message-title="{{sessionMessageTitle}}"
session-message-path="{{sessionMessagePath}}"
session-message-img="{{sessionMessageImg}}"
lang="{{lang}}"
hover-stop-propagation="{{hoverStopPropagation}}"
form-type="{{formType}}"
bindtap="handleClick"
bindgetuserinfo="handleGetuserinfo"
bindcontact="handleConcat"
bindgetphonenumber="handleGetphonenumber"
binderror="handleError"
bindlaunchapp="handleLaunchapp"
bindopensetting="handleOpensetting"
>
<view v-if="{{loading}}" class="wd-button__loading">
<view class="wd-button__loading-svg" style="background-image: url({{loadingIconSvg}});"></view>
</view>
<wd-icon v-if="{{icon}}" class="wd-button__icon" name="{{icon}}" ></wd-icon>
<view class="wd-button__text"><slot/></view>
</button>

View File

@ -0,0 +1,503 @@
@import "./../common/abstracts/_mixin.scss";
@import "./../common/abstracts/variable.scss";
@mixin button-type-style($color, $normal, $active, $disabled, $disabledcolor) {
background: $normal;
color: $color;
font-weight: $-fw-medium;
&::after {
border-color: $normal;
}
&.wd-button--active {
background: $active;
}
@include when(disabled) {
&.wd-button--active {
background: $disabled;
color: $disabledcolor;
}
background: $disabled;
color: $disabledcolor;
&::after {
border-color: $disabled;
}
}
@include when(loading) {
&,
&.wd-button--active {
color: $color;
background: $normal;
}
&::after {
border-color: $normal;
}
}
@include when(suck) {
border-radius: 0;
}
}
@mixin button-plain-style($color, $normal, $active, $disabled) {
color: $color;
background: transparent;
&::after {
border-color: $normal;
}
&.wd-button--active {
color: $active;
background: transparent;
&::after {
border-color: $active;
}
}
@include when(disabled) {
color: $disabled;
background: transparent;
&::after {
border-color: $disabled;
}
&.wd-button--active {
background: transparent;
&::after {
border-color: $disabled;
}
}
}
@include when(loading) {
&,
&.wd-button--active {
color: $color;
background: transparent;
&::after {
border-color: $normal;
}
}
}
}
@include b(button) {
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
outline: none;
-webkit-appearance: none;
outline: none;
background: transparent;
box-sizing: border-box;
border: none;
color: $-button-normal-color;
transition: all 0.2s;
user-select: none;
font-weight: normal;
&::after {
display: none;
}
&.wd-button--active {
color: $-button-normal-active-color;
background: $-button-normal-active-bg;
&::after {
border-color: $-button-normal-border-active-color;
}
}
@include when(disabled) {
color: $-button-normal-disabled-color;
background: $-button-normal-disabled-bg;
&::after {
border-color: $-button-normal-border-disabled-color;
}
&.wd-button--active {
color: $-button-normal-disabled-color;
background: $-button-normal-disabled-bg;
&::after {
border-color: $-button-normal-border-disabled-color;
}
}
}
@include e(loading) {
margin-right: 5px;
animation: wd-rotate 0.8s linear infinite;
animation-duration: 2s;
}
@include e(loading-svg) {
width: 100%;
height: 100%;
background-size: cover;
background-repeat: no-repeat;
}
@include when(loading) {
&.wd-button--active {
color: $-button-normal-color;
background: transparent;
&::after {
border-color: $-button-border-color;
}
}
}
@include when(primary) {
@include button-type-style(
$-color-white,
$-button-primary-bg-color,
$-button-primary-active-color,
$-button-primary-disabled-color,
$-color-white
);
}
@include when(success) {
@include button-type-style(
$-color-white,
$-button-success-color,
$-button-success-active-color,
$-button-success-disabled-color,
$-color-white
);
}
@include when(info) {
@include button-type-style(
$-button-info-color,
$-button-info-bg-color,
$-button-info-active-bg-color,
$-button-info-disabled-bg-color,
$-button-info-disabled-color
);
}
@include when(warning) {
@include button-type-style(
$-color-white,
$-button-warning-color,
$-button-warning-active-color,
$-button-warning-disabled-color,
$-color-white
);
}
@include when(error) {
@include button-type-style(
$-color-white,
$-button-error-color,
$-button-error-active-color,
$-button-error-disabled-color,
$-color-white
);
}
@include when(small) {
height: $-button-small-height;
padding: $-button-small-padding;
border-radius: $-button-small-radius;
font-size: $-button-small-fs;
font-weight: normal;
@include when(round) {
border-radius: calc($-button-small-height / 2);
&::after {
border-radius: $-button-small-height;
}
}
.wd-button__loading {
width: $-button-small-loading;
height: $-button-small-loading;
}
}
@include when(medium) {
height: $-button-medium-height;
padding: $-button-medium-padding;
border-radius: $-button-medium-radius;
font-size: $-button-medium-fs;
&::after {
border-radius: $-button-medium-radius * 2;
}
@include when(primary) {
box-shadow: $-button-medium-box-shadow-size $-button-primary-box-shadow-color;
}
@include when(success) {
box-shadow: $-button-medium-box-shadow-size $-button-success-box-shadow-color;
}
@include when(warning) {
box-shadow: $-button-medium-box-shadow-size $-button-warning-box-shadow-color;
}
@include when(error) {
box-shadow: $-button-medium-box-shadow-size $-button-error-box-shadow-color;
}
@include when(plain) {
box-shadow: none;
}
@include when(round) {
min-width: 118px;
border-radius: calc($-button-medium-height / 2);
&::after {
border-radius: $-button-medium-height;
}
@include when(icon) {
min-width: 0;
border-radius: 50%;
}
@include when(text) {
min-width: 0;
border-radius: 0;
}
}
.wd-button__loading {
width: $-button-medium-loading;
height: $-button-medium-loading;
}
}
@include when(large) {
height: $-button-large-height;
padding: $-button-large-padding;
border-radius: $-button-large-radius;
font-size: $-button-large-fs;
&::after {
border-radius: $-button-large-radius * 2;
}
&:not(.is-plain) {
@include when(primary) {
box-shadow: $-button-large-box-shadow-size $-button-primary-box-shadow-color;
}
@include when(success) {
box-shadow: $-button-large-box-shadow-size $-button-success-box-shadow-color;
}
@include when(warning) {
box-shadow: $-button-large-box-shadow-size $-button-warning-box-shadow-color;
}
@include when(error) {
box-shadow: $-button-large-box-shadow-size $-button-error-box-shadow-color;
}
}
@include when(round) {
border-radius: calc($-button-large-height / 2);
&::after {
border-radius: $-button-large-height;
}
@include when(icon) {
border-radius: 50%;
}
@include when(text) {
border-radius: 0;
}
}
.wd-button__loading {
width: $-button-large-loading;
height: $-button-large-loading;
}
}
@include when(text) {
color: $-button-primary-color;
padding: 4px 0;
&::after {
display: none;
}
&.wd-button--active {
color: $-button-primary-active-color;
background: transparent;
}
@include when(disabled) {
color: $-button-normal-disabled-color;
background: transparent;
}
}
@include when(plain) {
background: $-color-white;
&::after {
position: absolute;
display: block;
content: '';
width: 200%;
height: 200%;
left: 0;
top: 0;
border: 1px solid $-button-border-color;
box-sizing: border-box;
transform: scale(0.5);
transform-origin: left top;
}
@include when(primary) {
@include button-plain-style(
$-button-primary-color,
$-button-primary-color,
$-button-primary-active-color,
$-button-primary-disabled-color
);
&.wd-button--active {
background-color: $-button-primary-plain-active-bg-color;
}
@include when(disabled) {
&.wd-button--active {
background-color: $-button-primary-plain-active-bg-color;
}
opacity: 1;
background-color: $-button-primary-plain-active-bg-color;
color: $-button-primary-plain-disabled-color;
}
}
@include when(success) {
@include button-plain-style(
$-button-success-color,
$-button-success-color,
$-button-success-active-color,
$-button-success-disabled-color
);
}
@include when(info) {
@include button-plain-style(
$-button-info-plain-normal-color,
$-button-info-bg-color,
$-button-info-active-color,
$-button-info-disabled-color
);
&::after {
border-color: $-button-info-plain-border-color;
}
&.wd-button--active {
background-color: $-button-info-plain-active-bg-color;
&::after {
border-color: $-button-info-plain-active-color;
}
}
@include when(disabled) {
&,
&.wd-button--active {
background-color: $-button-info-plain-disabled-bg-color;
&::after {
border-color: $-button-info-plain-disabled-bg-color;
}
}
}
@include when(loading) {
&::after,
&.wd-button--active::after {
border-color: $-button-info-plain-border-color;
}
}
}
@include when(warning) {
@include button-plain-style(
$-button-warning-color,
$-button-warning-color,
$-button-warning-active-color,
$-button-warning-disabled-color
);
}
@include when(error) {
@include button-plain-style(
$-button-error-color,
$-button-error-color,
$-button-error-active-color,
$-button-error-disabled-color
);
}
@include when(suck) {
&.wd-button--active {
background: $-button-suck-active-color;
}
@include when(disabled) {
background: $-color-white;
}
}
}
@include when(suck) {
display: flex;
font-size: $-button-large-fs;
height: $-button-suck-height;
border-radius: 0;
&::after {
display: none;
}
}
@include when(block) {
display: flex;
}
@include when(icon) {
width: $-button-icon-size;
height: $-button-icon-size;
padding: 0;
border-radius: 50%;
font-size: 0;
color: $-button-icon-color;
&::after {
display: none;
}
&.wd-button--active {
background: $-button-icon-active-color;
}
.wd-button__icon {
margin-right: 0;
}
@include when(disabled) {
color: $-button-icon-disabled-color;
background: transparent;
&.wd-button--active {
background: transparent;
}
}
}
@include e(icon) {
display: block;
margin-right: 6px;
font-size: $-button-icon-fs;
vertical-align: middle;
}
@include e(text) {
user-select: none;
white-space: nowrap;
}
}
// 微信2.8.0以上版本加了较高层级的默认样式需要重置掉
.wd-button {
min-height: auto;
width: auto;
}
@keyframes wd-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,630 @@
<template>
<button
hover-class="wd-button--active"
:class="[
'wd-button',
'custom-class',
'is-' + type,
'is-' + size,
plain ? 'is-plain' : '',
disabled ? 'is-disabled' : '',
round ? 'is-round' : '',
suck ? 'is-suck' : '',
block ? 'is-block' : '',
loading ? 'is-loading' : ''
]"
:hover-start-time="hoverStartTime"
:hover-stay-time="hoverStayTime"
:open-type="openType"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:app-parameter="appParameter"
:show-message-card="showMessageCard"
:session-from="sessionFrom"
:lang="lang"
:hover-stop-propagation="hoverStopPropagation"
:form-type="formType"
@click="handleClick"
@getuserinfo="handleGetuserinfo"
@contact="handleConcat"
@getphonenumber="handleGetphonenumber"
@error="handleError"
@launchapp="handleLaunchapp"
@opensetting="handleOpensetting"
>
<view v-if="loading" class="wd-button__loading">
<view class="wd-button__loading-svg" :style="loadingStyle"></view>
</view>
<wd-icon v-if="icon" class="wd-button__icon" :name="icon"></wd-icon>
<view class="wd-button__text"><slot /></view>
</button>
</template>
<script lang="ts" setup>
import { computed, watch } from 'vue'
import { ref } from 'vue'
import base64 from '../common/base64'
const loadingIcon = (color = '#4D80F0', reverse = true) => {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 42"><defs><linearGradient x1="100%" y1="0%" x2="0%" y2="0%" id="a"><stop stop-color="${
reverse ? color : '#fff'
}" offset="0%" stop-opacity="0"/><stop stop-color="${
reverse ? color : '#fff'
}" offset="100%"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><path d="M21 1c11.046 0 20 8.954 20 20s-8.954 20-20 20S1 32.046 1 21 9.954 1 21 1zm0 7C13.82 8 8 13.82 8 21s5.82 13 13 13 13-5.82 13-13S28.18 8 21 8z" fill="${
reverse ? '#fff' : color
}"/><path d="M4.599 21c0 9.044 7.332 16.376 16.376 16.376 9.045 0 16.376-7.332 16.376-16.376" stroke="url(#a)" stroke-width="3.5" stroke-linecap="round"/></g></svg>`
}
type ButtonType = 'primary' | 'success' | 'info' | 'warning' | 'error' | 'default'
type ButtonSize = 'small' | 'medium' | 'large'
interface Props {
plain: boolean
disabled: boolean
round: {
type: boolean
value: true
}
suck: boolean
block: boolean
type: ButtonType
size: ButtonSize
icon: string
loading: boolean
loadingColor: string
openType: string
formType: string
hoverStopPropagation: boolean
lang: string
sessionFrom: string
sendMessageTitle: string
sendMessagePath: string
sendMessageImg: string
appParameter: string
showMessageCard: boolean
}
const props = withDefaults(defineProps<Props>(), {
type: 'primary',
size: 'medium'
})
const hoverStartTime = ref<number>(20)
const hoverStayTime = ref<number>(70)
const loadingIconSvg = ref<string>('')
watch(
() => props.loading,
() => {
buildLoadingSvg()
},
{ deep: true, immediate: true }
)
const loadingStyle = computed(() => {
return `background-image: url(${loadingIconSvg.value});`
})
const emit = defineEmits(['click', 'getuserinfo', 'contact', 'getphonenumber', 'error', 'launchapp', 'opensetting'])
function handleClick(event) {
if (!props.disabled && !props.loading) {
emit('click', event.detail)
}
}
function handleGetuserinfo(event) {
emit('getuserinfo', event.detail)
}
function handleConcat(event) {
emit('contact', event.detail)
}
function handleGetphonenumber(event) {
emit('getphonenumber', event.detail)
}
function handleError(event) {
emit('error', event.detail)
}
function handleLaunchapp(event) {
emit('launchapp', event.detail)
}
function handleOpensetting(event) {
emit('opensetting', event.detail)
}
function buildLoadingSvg() {
const { loadingColor, type, plain } = props
let color = loadingColor
if (!color) {
switch (type) {
case 'primary':
color = '#4D80F0'
break
case 'success':
color = '#34d19d'
break
case 'info':
color = '#333'
break
case 'warning':
color = '#f0883a'
break
case 'error':
color = '#fa4350'
break
case 'default':
color = '#333'
break
}
}
const svg = loadingIcon(color, !plain)
loadingIconSvg.value = `"data:image/svg+xml;base64,${base64(svg)}"`
}
</script>
<style lang="scss" scoped>
@import './../common/abstracts/_mixin.scss';
@import './../common/abstracts/variable.scss';
@mixin button-type-style($color, $normal, $active, $disabled, $disabledcolor) {
background: $normal;
color: $color;
font-weight: $-fw-medium;
&::after {
border-color: $normal;
}
&.wd-button--active {
background: $active;
}
@include when(disabled) {
&.wd-button--active {
background: $disabled;
color: $disabledcolor;
}
background: $disabled;
color: $disabledcolor;
&::after {
border-color: $disabled;
}
}
@include when(loading) {
&,
&.wd-button--active {
color: $color;
background: $normal;
}
&::after {
border-color: $normal;
}
}
@include when(suck) {
border-radius: 0;
}
}
@mixin button-plain-style($color, $normal, $active, $disabled) {
color: $color;
background: transparent;
&::after {
border-color: $normal;
}
&.wd-button--active {
color: $active;
background: transparent;
&::after {
border-color: $active;
}
}
@include when(disabled) {
color: $disabled;
background: transparent;
&::after {
border-color: $disabled;
}
&.wd-button--active {
background: transparent;
&::after {
border-color: $disabled;
}
}
}
@include when(loading) {
&,
&.wd-button--active {
color: $color;
background: transparent;
&::after {
border-color: $normal;
}
}
}
}
@include b(button) {
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
outline: none;
-webkit-appearance: none;
outline: none;
background: transparent;
box-sizing: border-box;
border: none;
color: $-button-normal-color;
transition: all 0.2s;
user-select: none;
font-weight: normal;
&::after {
display: none;
}
&.wd-button--active {
color: $-button-normal-active-color;
background: $-button-normal-active-bg;
&::after {
border-color: $-button-normal-border-active-color;
}
}
@include when(disabled) {
color: $-button-normal-disabled-color;
background: $-button-normal-disabled-bg;
&::after {
border-color: $-button-normal-border-disabled-color;
}
&.wd-button--active {
color: $-button-normal-disabled-color;
background: $-button-normal-disabled-bg;
&::after {
border-color: $-button-normal-border-disabled-color;
}
}
}
@include e(loading) {
margin-right: 5px;
animation: wd-rotate 0.8s linear infinite;
animation-duration: 2s;
}
@include e(loading-svg) {
width: 100%;
height: 100%;
background-size: cover;
background-repeat: no-repeat;
}
@include when(loading) {
&.wd-button--active {
color: $-button-normal-color;
background: transparent;
&::after {
border-color: $-button-border-color;
}
}
}
@include when(primary) {
@include button-type-style(
$-color-white,
$-button-primary-bg-color,
$-button-primary-active-color,
$-button-primary-disabled-color,
$-color-white
);
}
@include when(success) {
@include button-type-style($-color-white, $-button-success-color, $-button-success-active-color, $-button-success-disabled-color, $-color-white);
}
@include when(info) {
@include button-type-style(
$-button-info-color,
$-button-info-bg-color,
$-button-info-active-bg-color,
$-button-info-disabled-bg-color,
$-button-info-disabled-color
);
}
@include when(warning) {
@include button-type-style($-color-white, $-button-warning-color, $-button-warning-active-color, $-button-warning-disabled-color, $-color-white);
}
@include when(error) {
@include button-type-style($-color-white, $-button-error-color, $-button-error-active-color, $-button-error-disabled-color, $-color-white);
}
@include when(small) {
height: $-button-small-height;
padding: $-button-small-padding;
border-radius: $-button-small-radius;
font-size: $-button-small-fs;
font-weight: normal;
@include when(round) {
border-radius: calc($-button-small-height / 2);
&::after {
border-radius: $-button-small-height;
}
}
.wd-button__loading {
width: $-button-small-loading;
height: $-button-small-loading;
}
}
@include when(medium) {
height: $-button-medium-height;
padding: $-button-medium-padding;
border-radius: $-button-medium-radius;
font-size: $-button-medium-fs;
&::after {
border-radius: $-button-medium-radius * 2;
}
@include when(primary) {
box-shadow: $-button-medium-box-shadow-size $-button-primary-box-shadow-color;
}
@include when(success) {
box-shadow: $-button-medium-box-shadow-size $-button-success-box-shadow-color;
}
@include when(warning) {
box-shadow: $-button-medium-box-shadow-size $-button-warning-box-shadow-color;
}
@include when(error) {
box-shadow: $-button-medium-box-shadow-size $-button-error-box-shadow-color;
}
@include when(plain) {
box-shadow: none;
}
@include when(round) {
min-width: 118px;
border-radius: calc($-button-medium-height / 2);
&::after {
border-radius: $-button-medium-height;
}
@include when(icon) {
min-width: 0;
border-radius: 50%;
}
@include when(text) {
min-width: 0;
border-radius: 0;
}
}
.wd-button__loading {
width: $-button-medium-loading;
height: $-button-medium-loading;
}
}
@include when(large) {
height: $-button-large-height;
padding: $-button-large-padding;
border-radius: $-button-large-radius;
font-size: $-button-large-fs;
&::after {
border-radius: $-button-large-radius * 2;
}
&:not(.is-plain) {
@include when(primary) {
box-shadow: $-button-large-box-shadow-size $-button-primary-box-shadow-color;
}
@include when(success) {
box-shadow: $-button-large-box-shadow-size $-button-success-box-shadow-color;
}
@include when(warning) {
box-shadow: $-button-large-box-shadow-size $-button-warning-box-shadow-color;
}
@include when(error) {
box-shadow: $-button-large-box-shadow-size $-button-error-box-shadow-color;
}
}
@include when(round) {
border-radius: calc($-button-large-height / 2);
&::after {
border-radius: $-button-large-height;
}
@include when(icon) {
border-radius: 50%;
}
@include when(text) {
border-radius: 0;
}
}
.wd-button__loading {
width: $-button-large-loading;
height: $-button-large-loading;
}
}
@include when(text) {
color: $-button-primary-color;
padding: 4px 0;
&::after {
display: none;
}
&.wd-button--active {
color: $-button-primary-active-color;
background: transparent;
}
@include when(disabled) {
color: $-button-normal-disabled-color;
background: transparent;
}
}
@include when(plain) {
background: $-color-white;
&::after {
position: absolute;
display: block;
content: '';
width: 200%;
height: 200%;
left: 0;
top: 0;
border: 1px solid $-button-border-color;
box-sizing: border-box;
transform: scale(0.5);
transform-origin: left top;
}
@include when(primary) {
@include button-plain-style($-button-primary-color, $-button-primary-color, $-button-primary-active-color, $-button-primary-disabled-color);
&.wd-button--active {
background-color: $-button-primary-plain-active-bg-color;
}
@include when(disabled) {
&.wd-button--active {
background-color: $-button-primary-plain-active-bg-color;
}
opacity: 1;
background-color: $-button-primary-plain-active-bg-color;
color: $-button-primary-plain-disabled-color;
}
}
@include when(success) {
@include button-plain-style($-button-success-color, $-button-success-color, $-button-success-active-color, $-button-success-disabled-color);
}
@include when(info) {
@include button-plain-style($-button-info-plain-normal-color, $-button-info-bg-color, $-button-info-active-color, $-button-info-disabled-color);
&::after {
border-color: $-button-info-plain-border-color;
}
&.wd-button--active {
background-color: $-button-info-plain-active-bg-color;
&::after {
border-color: $-button-info-plain-active-color;
}
}
@include when(disabled) {
&,
&.wd-button--active {
background-color: $-button-info-plain-disabled-bg-color;
&::after {
border-color: $-button-info-plain-disabled-bg-color;
}
}
}
@include when(loading) {
&::after,
&.wd-button--active::after {
border-color: $-button-info-plain-border-color;
}
}
}
@include when(warning) {
@include button-plain-style($-button-warning-color, $-button-warning-color, $-button-warning-active-color, $-button-warning-disabled-color);
}
@include when(error) {
@include button-plain-style($-button-error-color, $-button-error-color, $-button-error-active-color, $-button-error-disabled-color);
}
@include when(suck) {
&.wd-button--active {
background: $-button-suck-active-color;
}
@include when(disabled) {
background: $-color-white;
}
}
}
@include when(suck) {
display: flex;
font-size: $-button-large-fs;
height: $-button-suck-height;
border-radius: 0;
&::after {
display: none;
}
}
@include when(block) {
display: flex;
}
@include when(icon) {
width: $-button-icon-size;
height: $-button-icon-size;
padding: 0;
border-radius: 50%;
font-size: 0;
color: $-button-icon-color;
&::after {
display: none;
}
&.wd-button--active {
background: $-button-icon-active-color;
}
.wd-button__icon {
margin-right: 0;
}
@include when(disabled) {
color: $-button-icon-disabled-color;
background: transparent;
&.wd-button--active {
background: transparent;
}
}
}
@include e(icon) {
display: block;
margin-right: 6px;
font-size: $-button-icon-fs;
vertical-align: middle;
}
@include e(text) {
user-select: none;
white-space: nowrap;
}
}
// 2.8.0
.wd-button {
min-height: auto;
width: auto;
}
@keyframes wd-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>

View File

@ -0,0 +1,320 @@
import VueComponent from '../../../common/component'
import { compareDate, getMonthEndDay, getWeekRange, getDayOffset, getDayByOffset, getDateByDefaultTime } from '../../utils'
import { getType } from '../../../common/util'
import Toast from '../../../toast/toast.js'
VueComponent({
data: {
days: []
},
props: {
type: {
type: String,
observer: 'setDays'
},
date: {
type: Number,
observer: 'setDays'
},
value: {
type: [null, Number, Array],
observer: 'setDays'
},
minDate: {
type: Number,
observer: 'setDays'
},
maxDate: {
type: Number,
observer: 'setDays'
},
firstDayOfWeek: Number,
formatter: {
type: null,
observer: 'setDays'
},
maxRange: Number,
rangePrompt: String,
allowSameDay: Boolean,
defaultTime: Array
},
methods: {
setDays () {
const days = []
const date = new Date(this.data.date)
const year = date.getFullYear()
const month = date.getMonth()
const totalDay = getMonthEndDay(year, month + 1)
let value = this.data.value
if ((this.data.type === 'week' || this.data.type === 'weekrange') && value) {
value = this.getWeekValue()
}
for (let day = 1; day <= totalDay; day++) {
const date = new Date(year, month, day).getTime()
let type = this.getDayType(date, value)
if (!type && compareDate(date, Date.now()) === 0) {
type = 'current'
}
const dayObj = this.getFormatterDate(date, day, type)
days.push(dayObj)
}
this.setData({
days
})
},
getDayType (date, value) {
switch (this.data.type) {
case 'date':
case 'datetime':
return this.getDateType(date)
case 'dates':
return this.getDatesType(date)
case 'daterange':
case 'datetimerange':
return this.getDatetimeType(date, value)
case 'week':
return this.getWeektimeType(date, value)
case 'weekrange':
return this.getWeektimeType(date, value)
default:
return this.getDateType(date)
}
},
getDateType (date) {
if (this.data.value && compareDate(date, this.data.value) === 0) {
return 'selected'
}
return ''
},
getDatesType (date) {
if (!this.data.value) return ''
let type = ''
this.data.value.some((item) => {
if (compareDate(date, item) === 0) {
type = 'selected'
return true
}
return false
})
return type
},
getDatetimeType (date, value) {
const [startDate, endDate] = value || []
if (startDate && compareDate(date, startDate) === 0) {
if (this.data.allowSameDay && endDate && compareDate(startDate, endDate) === 0) {
return 'same'
}
return 'start'
} else if (endDate && compareDate(date, endDate) === 0) {
return 'end'
} else if (startDate && endDate && compareDate(date, startDate) === 1 && compareDate(date, endDate) === -1) {
return 'middle'
} else {
return ''
}
},
getWeektimeType (date, value) {
const [startDate, endDate] = value || []
if (startDate && compareDate(date, startDate) === 0) {
return 'start'
} else if (endDate && compareDate(date, endDate) === 0) {
return 'end'
} else if (startDate && endDate && compareDate(date, startDate) === 1 && compareDate(date, endDate) === -1) {
return 'middle'
} else {
return ''
}
},
getWeekValue () {
if (this.data.type === 'week') {
return getWeekRange(this.data.value, this.data.firstDayOfWeek)
} else {
const [startDate, endDate] = this.data.value || []
if (startDate) {
const firstWeekRange = getWeekRange(startDate, this.data.firstDayOfWeek)
if (endDate) {
const endWeekRange = getWeekRange(endDate, this.data.firstDayOfWeek)
return [firstWeekRange[0], endWeekRange[1]]
} else {
return firstWeekRange
}
}
return []
}
},
handleDateClick (event) {
const { index } = event.currentTarget.dataset
const date = this.data.days[index]
switch (this.data.type) {
case 'date':
case 'datetime':
this.handleDateChange(date)
break
case 'dates':
this.handleDatesChange(date)
break
case 'daterange':
case 'datetimerange':
this.handleDateRangeChange(date)
break
case 'week':
this.handleWeekChange(date)
break
case 'weekrange':
this.handleWeekRangeChange(date)
break
default:
this.handleDateChange(date)
}
},
getDate (date, isEnd) {
date = this.data.defaultTime && this.data.defaultTime.length > 0
? getDateByDefaultTime(date, isEnd ? this.data.defaultTime[1] : this.data.defaultTime[0])
: date
if (date < this.data.minDate) return this.data.minDate
if (date > this.data.maxDate) return this.data.maxDate
return date
},
handleDateChange (date) {
if (date.disabled) return
if (date.type !== 'selected') {
this.$emit('change', {
value: this.getDate(date.date),
type: 'start'
})
}
},
handleDatesChange (date) {
if (date.disabled) return
const value = this.data.value || []
if (date.type !== 'selected') {
value.push(this.getDate(date.date))
} else {
value.splice(value.indexOf(date.date), 1)
}
this.$emit('change', {
value
})
},
handleDateRangeChange (date) {
if (date.disabled) return
let value
let type
const [startDate, endDate] = this.data.value || []
const compare = compareDate(date.date, startDate)
// 禁止选择同个日期
if (!this.data.allowSameDay && compare === 0 && (this.data.type === 'daterange' || this.data.type === 'datetimerange') && !endDate) {
return
}
if (startDate && !endDate && compare > -1) {
// 不能选择超过最大范围的日期
if (this.data.maxRange && getDayOffset(date.date, startDate) > this.data.maxRange) {
const maxEndDate = getDayByOffset(startDate, this.data.maxRange - 1)
value = [startDate, this.getDate(maxEndDate, true)]
Toast({
msg: this.data.rangePrompt || `选择天数不能超过${this.data.maxRange}`,
context: this
})
} else {
value = [startDate, this.getDate(date.date, true)]
}
} else if (this.data.type === 'datetimerange' && startDate && endDate) {
// 时间范围类型,且有开始时间和结束时间,需要支持重新点击开始日期和结束日期可以重新修改时间
if (compare === 0) {
type = 'start'
value = this.data.value
} else if (compareDate(date.date, endDate) === 0) {
type = 'end'
value = this.data.value
} else {
value = [this.getDate(date.date), null]
}
} else {
value = [this.getDate(date.date), null]
}
this.$emit('change', {
value,
type: type || (value[1] ? 'end' : 'start')
})
},
handleWeekChange (date) {
const [weekStart] = getWeekRange(date.date, this.data.firstDayOfWeek)
// 周的第一天如果是禁用状态,则不可选中
if (this.getFormatterDate(weekStart, new Date(weekStart).getDate()).disabled) return
this.$emit('change', {
value: this.getDate(weekStart) + 24 * 60 * 60 * 1000
})
},
handleWeekRangeChange (date) {
const [weekStartDate] = getWeekRange(date.date, this.data.firstDayOfWeek)
// 周的第一天如果是禁用状态,则不可选中
if (this.getFormatterDate(weekStartDate, new Date(weekStartDate).getDate()).disabled) return
let value
const [startDate, endDate] = this.data.value || []
const [startWeekStartDate] = startDate ? getWeekRange(startDate, this.data.firstDayOfWeek) : []
const compare = compareDate(weekStartDate, startWeekStartDate)
if (startDate && !endDate && compare > -1) {
if (!this.data.allowSameDay && compare === 0) return
value = [this.getDate(startWeekStartDate) + 24 * 60 * 60 * 1000, this.getDate(weekStartDate) + 24 * 60 * 60 * 1000]
} else {
value = [this.getDate(weekStartDate) + 24 * 60 * 60 * 1000, null]
}
this.$emit('change', {
value
})
},
getFormatterDate (date, day, type) {
let dayObj = {
date: date,
text: day,
topInfo: '',
bottomInfo: '',
type,
disabled: compareDate(date, this.data.minDate) === -1 || compareDate(date, this.data.maxDate) === 1
}
if (this.data.formatter) {
if (getType(this.data.formatter) === 'function') {
dayObj = this.data.formatter(dayObj)
} else {
console.error('[wot-design] error(wd-calendar-view): the formatter prop of wd-calendar-view should be a function')
}
}
return dayObj
}
}
})

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"wd-toast": "../../../toast/index"
}
}

View File

@ -0,0 +1,25 @@
<jds src="../../utils.jds" module="utils" />
<wd-toast id="wd-toast" />
<view class="wd-month">
<view class="wd-month__title">{{ utils.formatMonthTitle(date) }}</view>
<view class="wd-month__days">
<view
jd:for="{{ days }}"
jd:key="date"
class="wd-month__day {{ item.disabled ? 'is-disabled' : '' }} {{ item.type ? utils.getItemClass(item.type, value, type) : '' }}"
style="{{ utils.getFirstDayStyle(index, item.date, firstDayOfWeek) }}"
data-index="{{ index }}"
catchtap="handleDateClick"
>
<view class="wd-month__day-container">
<view class="wd-month__day-top">{{ item.topInfo }}</view>
<view class="wd-month__day-text">
{{ item.text }}
</view>
<view class="wd-month__day-bottom">{{ item.bottomInfo }}</view>
</view>
</view>
</view>
</view>

View File

@ -0,0 +1,114 @@
@import '../../../common/abstracts/variable';
@import '../../../common/abstracts/mixin';
@include b(month) {
@include e(title) {
padding: 13px 0;
text-align: center;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
}
@include e(days) {
display: flex;
flex-wrap: wrap;
font-size: $-calendar-day-fs;
color: $-calendar-day-color;
}
@include e(day) {
position: relative;
width: 14.285%;
height: $-calendar-day-height;
line-height: $-calendar-day-height;
text-align: center;
@include when(disabled) {
.wd-month__day-text {
color: $-calendar-disabled-color;
}
}
@include when(current) {
color: $-calendar-active-color;
}
@include when(selected) {
.wd-month__day-container {
border-radius: $-calendar-active-border;
background: $-calendar-active-color;
color: #fff;
}
}
@include when(middle) {
.wd-month__day-container {
background: $-calendar-range-color;
}
}
@include when(start) {
&::after {
position: absolute;
content: '';
height: $-calendar-day-height;
top: 0;
right: 0;
left: 50%;
background: $-calendar-range-color;
z-index: 1;
}
&.is-without-end::after {
display: none;
}
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
border-radius: $-calendar-active-border;
}
}
@include when(end) {
&::after {
position: absolute;
content: '';
height: $-calendar-day-height;
top: 0;
left: 0;
right: 50%;
background: $-calendar-range-color;
z-index: 1;
}
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
border-radius: $-calendar-active-border;
}
}
@include when(same) {
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
border-radius: $-calendar-active-border;
}
}
}
@include e(day-container) {
position: relative;
z-index: 2;
}
@include e(day-text) {
font-weight: $-calendar-day-fw;
}
@include e(day-top) {
position: absolute;
top: 10px;
left: 0;
right: 0;
line-height: 1.1;
font-size: $-calendar-info-fs;
text-align: center;
}
@include e(day-bottom) {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
line-height: 1.1;
font-size: $-calendar-info-fs;
text-align: center;
}
}

View File

@ -0,0 +1,242 @@
import VueComponent from '../../../common/component'
import { formatMonthTitle, getMonths, compareMonth, getTimeData } from '../../utils'
import { getType, debounce, isEqual } from '../../../common/util'
VueComponent({
props: {
type: {
type: String,
observer (val) {
if ((val === 'datetime' && this.data.value) || (val === 'datetimerange' && this.data.value && this.data.value.length > 0 && this.data.value[0])) {
this.setTime(this.data.value, 'start')
}
}
},
value: {
type: [null, Number, Array],
observer (val, oldVal) {
if (isEqual(val, this.data.innerValue)) return
if ((this.data.type === 'datetime' && val) || (this.data.type === 'datetimerange' && val && val.length > 0 && val[0])) {
this.setTime(val, 'start')
}
}
},
minDate: Number,
maxDate: Number,
firstDayOfWeek: Number,
formatter: null,
maxRange: Number,
rangePrompt: String,
allowSameDay: Boolean,
showPanelTitle: Boolean,
defaultTime: Array,
panelHeight: Number,
timeFilter: null,
hideSecond: Boolean
},
data: {
title: '',
scrollIntoView: '',
timeValue: [],
timeData: [],
timeType: '', // 当前时间类型,是开始还是结束
innerValue: '' // 内部保存一个值,用于判断新老值,避免监听器触发
},
mounted () {
this.initRect()
this.scrollIntoView()
},
methods: {
initRect (thresholds = [0, 0.7, 0.8, 0.9, 1]) {
if (!this.data.showPanelTitle) return
if (this.contentObserver != null) {
this.contentObserver.disconnect()
}
const contentObserver = this.createIntersectionObserver({
thresholds,
observeAll: true
})
this.contentObserver = contentObserver
contentObserver.relativeTo('.wd-month-panel__container')
contentObserver.observe('.month', (res) => {
if (res.boundingClientRect.top <= res.relativeRect.top) {
this.setData({
title: formatMonthTitle(res.dataset.date)
})
}
})
},
scrollIntoView () {
setTimeout(() => {
let activeDate
const type = getType(this.data.value)
if (type === 'array') {
activeDate = this.data.value[0]
} else if (type === 'number') {
activeDate = this.data.value
}
if (!activeDate) {
activeDate = Date.now()
}
const months = getMonths(this.data.minDate, this.data.maxDate)
months.some((month, index) => {
if (compareMonth(month, activeDate) === 0) {
this.setData({
scrollIntoView: `month${index}`
})
return true
}
return false
})
}, 50)
},
/**
* 获取时间 picker 的数据
* @param {timestamp|array} value 当前时间
* @param {string} type 类型是开始还是结束
*/
getTimeData (value, type) {
if (this.data.type === 'datetime') {
return getTimeData({
date: value,
minDate: this.data.minDate,
maxDate: this.data.maxDate,
filter: this.data.timeFilter,
isHideSecond: this.data.hideSecond
})
} else {
if (type === 'start') {
return getTimeData({
date: value[0],
minDate: this.data.minDate,
maxDate: this.data.value[1] ? this.data.value[1] : this.data.maxDate,
filter: this.data.timeFilter,
isHideSecond: this.data.hideSecond
})
} else {
return getTimeData({
date: value[1],
minDate: value[0],
maxDate: this.data.maxDate,
filter: this.data.timeFilter,
isHideSecond: this.data.hideSecond
})
}
}
},
/**
* 获取 date 的时分秒
* @param {timestamp} date 时间
* @param {string} type 类型是开始还是结束
*/
getTimeValue (date, type) {
if (this.data.type === 'datetime') {
date = new Date(date)
} else {
if (type === 'start') {
date = new Date(date[0])
} else {
date = new Date(date[1])
}
}
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return this.data.hideSecond ? [hour, minute] : [hour, minute, second]
},
setTime (value, type) {
if (getType(value) === 'array' && value[0] && value[1] && type === 'start' && this.data.timeType === 'start') {
type = 'end'
}
this.setData({
timeData: this.getTimeData(value, type),
timeValue: this.getTimeValue(value, type),
timeType: type
}, () => {
// 重新设置 thresholds
this.initRect([0, 0.58, 0.69, 0.83, 1])
})
},
handleDateChange ({ detail: { value, type } }) {
if (!isEqual(value, this.data.value)) {
// 内部保存一个值,用于判断新老值,避免监听器触发
this.setData({
innerValue: value
})
this.handleChange(value)
}
// datetime 和 datetimerange 类型,需要计算 timeData 并做展示
if (this.data.type.indexOf('time') > -1) {
this.setTime(value, type)
}
},
handleChange (value) {
this.$emit('change', {
value
})
},
handleTimeChange (event) {
const { value } = event.detail
if (this.data.type === 'datetime') {
const date = new Date(this.data.value)
date.setHours(value[0])
date.setMinutes(value[1])
date.setSeconds(this.data.hideSecond ? 0 : value[2])
const dateTime = date.getTime()
this.setData({
timeData: this.getTimeData(dateTime),
timeValue: value
})
this.handleChange(dateTime)
} else {
const [start, end] = this.data.value
const dataValue = this.data.timeType === 'start' ? start : end
const date = new Date(dataValue)
date.setHours(value[0])
date.setMinutes(value[1])
date.setSeconds(this.data.hideSecond ? 0 : value[2])
const dateTime = date.getTime()
if (dateTime === dataValue) return
const finalValue = [start, end]
if (this.data.timeType === 'start') {
finalValue[0] = dateTime
} else {
finalValue[1] = dateTime
}
this.setData({
timeData: this.getTimeData(finalValue, this.data.timeType),
timeValue: value,
innerValue: finalValue // 内部保存一个值,用于判断新老值,避免监听器触发
})
this.handleChange(finalValue)
}
},
handlePickStart () {
this.$emit('pickstart')
},
handlePickEnd () {
this.$emit('pickend')
}
},
beforeCreate () {
this.handleChange = debounce(this.handleChange, 50)
}
})

View File

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"month": "../month/index",
"wd-picker-view": "../../../pickerView/index"
}
}

View File

@ -0,0 +1,51 @@
<jds src="../../utils.jds" module="utils" />
<view class="wd-month-panel">
<view v-if="{{ showPanelTitle }}" class="wd-month-panel__title">
{{ title }}
</view>
<view class="wd-month-panel__weeks">
<view jd:for="{{ 7 }}" jd:key="*this" class="wd-month-panel__week">{{ utils.getWeekLabel(item + firstDayOfWeek) }}</view>
</view>
<scroll-view
class="wd-month-panel__container {{ !!timeType ? 'wd-month-panel__container--time' : '' }}"
style="height: {{ !!timeType ? ((panelHeight || 378) - 125) : (panelHeight || 378) }}px"
scroll-y
scroll-into-view="{{ scrollIntoView }}"
>
<month
jd:for="{{ utils.getMonths(minDate, maxDate) }}"
jd:key="*this"
id="month{{index}}"
class="month"
type="{{ type }}"
date="{{ item }}"
data-date="{{ item }}"
value="{{ value }}"
min-date="{{ minDate }}"
max-date="{{ maxDate }}"
first-day-of-week="{{ firstDayOfWeek }}"
formatter="{{ formatter }}"
max-range="{{ maxRange }}"
range-prompt="{{ rangePrompt }}"
allow-same-day="{{ allowSameDay }}"
default-time="{{ defaultTime }}"
bind:change="handleDateChange"
/>
</scroll-view>
<view v-if="{{ !!timeType }}" class="wd-month-panel__time">
<view v-if="{{ type === 'datetimerange' }}" class="wd-month-panel__time-label">
<view class="wd-month-panel__time-text">{{ timeType === 'start' ? '开始' : '结束' }}</view>
</view>
<view class="wd-month-panel__time-picker">
<wd-picker-view
value="{{ timeValue }}"
columns="{{ timeData }}"
columns-height="{{ 125 }}"
bind:change="handleTimeChange"
bind:pickstart="handlePickStart"
bind:pickend="handlePickEnd"
/>
</view>
</view>
</view>

View File

@ -0,0 +1,62 @@
@import '../../../common/abstracts/variable';
@import '../../../common/abstracts/mixin';
@include b(month-panel) {
font-size: $-calendar-fs;
@include e(title) {
padding: 5px 0;
text-align: center;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
padding: $-calendar-panel-padding;
}
@include e(weeks) {
display: flex;
height: $-calendar-week-height;
line-height: $-calendar-week-height;
box-shadow: 0px 4px 8px 0 rgba(0, 0, 0, 0.02);
color: $-calendar-week-color;
font-size: $-calendar-week-fs;
padding: $-calendar-panel-padding;
}
@include e(week) {
flex: 1;
text-align: center;
}
@include e(container) {
padding: $-calendar-panel-padding;
box-sizing: border-box;
}
@include e(time) {
display: flex;
box-shadow: 0px -4px 8px 0px rgba(0, 0, 0, 0.02);
}
@include e(time-label) {
position: relative;
flex: 1;
font-size: $-picker-column-fs;
text-align: center;
line-height: 125px;
color: $-picker-column-color;
&::after {
position: absolute;
content: '';
height: 35px;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
background: $-picker-column-select-bg;
z-index: 0;
}
}
@include e(time-text) {
position: relative;
z-index: 1;
}
@include e(time-picker) {
flex: 3;
}
}

View File

@ -0,0 +1,165 @@
import VueComponent from '../../../common/component'
import { compareMonth, getMonthOffset, getMonthByOffset, getDateByDefaultTime } from '../../utils'
import { getType } from '../../../common/util'
import Toast from '../../../toast/toast.js'
VueComponent({
data: {
months: []
},
props: {
type: {
type: String,
observer: 'setMonths'
},
date: {
type: Number,
observer: 'setMonths'
},
value: {
type: [null, Number, Array],
observer: 'setMonths'
},
minDate: {
type: Number,
observer: 'setMonths'
},
maxDate: {
type: Number,
observer: 'setMonths'
},
formatter: {
type: null,
observer: 'setMonths'
},
maxRange: Number,
rangePrompt: String,
allowSameDay: Boolean,
defaultTime: Array
},
methods: {
setMonths () {
const months = []
const date = new Date(this.data.date)
const year = date.getFullYear()
const value = this.data.value
const valueType = getType(value)
if (this.data.type.indexOf('range') > -1 && value && valueType !== 'array') {
console.error('[wot-design] value should be array when type is range')
return
}
for (let month = 0; month < 12; month++) {
const date = new Date(year, month, 1).getTime()
let type = this.getMonthType(date, value)
if (!type && compareMonth(date, Date.now()) === 0) {
type = 'current'
}
const monthObj = this.getFormatterDate(date, month, type)
months.push(monthObj)
}
this.setData({
months
})
},
getMonthType (date) {
if (this.data.type === 'monthrange') {
const [startDate, endDate] = this.data.value || []
if (startDate && compareMonth(date, startDate) === 0) {
if (endDate && compareMonth(startDate, endDate) === 0) {
return 'same'
}
return 'start'
} else if (endDate && compareMonth(date, endDate) === 0) {
return 'end'
} else if (startDate && endDate && compareMonth(date, startDate) === 1 && compareMonth(date, endDate) === -1) {
return 'middle'
} else {
return ''
}
} else {
if (this.data.value && compareMonth(date, this.data.value) === 0) {
return 'selected'
} else {
return ''
}
}
},
handleDateClick (event) {
const { index } = event.currentTarget.dataset
const date = this.data.months[index]
if (date.disabled) return
switch (this.data.type) {
case 'month':
this.handleMonthChange(date)
break
case 'monthrange':
this.handleMonthRangeChange(date)
break
default:
this.handleMonthChange(date)
}
},
getDate (date) {
return this.data.defaultTime && this.data.defaultTime.length > 0 ? getDateByDefaultTime(date, this.data.defaultTime[0]) : date
},
handleMonthChange (date) {
if (date.type !== 'selected') {
this.$emit('change', {
value: this.getDate(date.date)
})
}
},
handleMonthRangeChange (date) {
let value
const [startDate, endDate] = this.data.value || []
const compare = compareMonth(date.date, startDate)
// 禁止选择同个日期
if (!this.data.allowSameDay && !endDate && compare === 0) return
if (startDate && !endDate && compare > -1) {
if (this.data.maxRange && getMonthOffset(date.date, startDate) > this.data.maxRange) {
const maxEndDate = getMonthByOffset(startDate, this.data.maxRange - 1)
value = [startDate, this.getDate(maxEndDate)]
Toast({
msg: this.data.rangePrompt || `选择月份不能超过${this.data.maxRange}个月`,
context: this
})
} else {
value = [startDate, this.getDate(date.date)]
}
} else {
value = [this.getDate(date.date), null]
}
this.$emit('change', {
value
})
},
getFormatterDate (date, month, type) {
let monthObj = {
date: date,
text: month + 1,
topInfo: '',
bottomInfo: '',
type,
disabled: compareMonth(date, this.data.minDate) === -1 || compareMonth(date, this.data.maxDate) === 1
}
if (this.data.formatter) {
if (getType(this.data.formatter) === 'function') {
monthObj = this.data.formatter(monthObj)
} else {
console.error('[wot-design] error(wd-calendar-view): the formatter prop of wd-calendar-view should be a function')
}
}
return monthObj
}
}
})

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"wd-toast": "../../../toast/index"
}
}

View File

@ -0,0 +1,22 @@
<jds src="../../utils.jds" module="utils" />
<wd-toast id="wd-toast" />
<view class="wd-year">
<view class="wd-year__title">{{ utils.formatYearTitle(date) }}</view>
<view class="wd-year__months">
<view
jd:for="{{ months }}"
jd:key="date"
class="wd-year__month {{ item.disabled ? 'is-disabled' : '' }} {{ item.type ? utils.getItemClass(item.type, value, type) : '' }}"
data-index="{{ index }}"
catchtap="handleDateClick"
>
<view class="wd-year__month-top">{{ item.topInfo }}</view>
<view class="wd-year__month-text">
{{ item.text }}月
</view>
<view class="wd-year__month-bottom">{{ item.bottomInfo }}</view>
</view>
</view>
</view>

View File

@ -0,0 +1,112 @@
@import '../../../common/abstracts/variable';
@import '../../../common/abstracts/mixin';
@include b(year) {
@include e(title) {
padding: 13px 0;
text-align: center;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
}
@include e(months) {
display: flex;
flex-wrap: wrap;
font-size: $-calendar-day-fs;
color: $-calendar-day-color;
}
@include e(month) {
position: relative;
width: 25%;
height: $-calendar-day-height;
line-height: $-calendar-day-height;
text-align: center;
@include when(disabled) {
.wd-year__month-text {
color: $-calendar-disabled-color;
}
}
@include when(current) {
color: $-calendar-active-color;
}
@include when(selected) {
color: #fff;
.wd-year__month-text {
border-radius: $-calendar-active-border;
background: $-calendar-active-color;
}
}
@include when(middle) {
background: $-calendar-range-color;
}
@include when(start) {
color: #fff;
&::after {
position: absolute;
top: 0;
right: 0;
left: 50%;
bottom: 0;
content: '';
background: $-calendar-range-color;
}
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
}
&.is-without-end::after {
display: none;
}
}
@include when(end) {
color: #fff;
&::after {
position: absolute;
top: 0;
left: 0;
right: 50%;
bottom: 0;
content: '';
background: $-calendar-range-color;
}
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
}
}
@include when(same) {
color: #fff;
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
}
}
}
@include e(month-text) {
width: $-calendar-month-width;
margin: 0 auto;
text-align: center;
}
@include e(month-top) {
position: absolute;
top: 10px;
left: 0;
right: 0;
line-height: 1.1;
font-size: $-calendar-info-fs;
text-align: center;
}
@include e(month-bottom) {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
line-height: 1.1;
font-size: $-calendar-info-fs;
text-align: center;
}
}

View File

@ -0,0 +1,87 @@
import VueComponent from '../../../common/component'
import { getYears, compareYear, formatYearTitle } from '../../utils'
import { getType } from '../../../common/util'
VueComponent({
props: {
type: String,
value: {
type: [null, Number, Array]
},
minDate: Number,
maxDate: Number,
formatter: null,
maxRange: Number,
rangePrompt: String,
allowSameDay: Boolean,
showPanelTitle: Boolean,
defaultTime: Array,
panelHeight: Number
},
data: {
title: '',
scrollIntoView: ''
},
mounted () {
this.initRect()
this.scrollIntoView()
},
methods: {
initRect (thresholds = [0, 0.15, 0.7, 0.8, 0.9, 1]) {
if (!this.data.showPanelTitle) return
if (this.contentObserver != null) {
this.contentObserver.disconnect()
}
const contentObserver = this.createIntersectionObserver({
thresholds,
observeAll: true
})
this.contentObserver = contentObserver
contentObserver.relativeTo('.wd-year-panel__container')
contentObserver.observe('.year', (res) => {
if (res.boundingClientRect.top <= res.relativeRect.top) {
this.setData({
title: formatYearTitle(res.dataset.date)
})
}
})
},
scrollIntoView () {
this.requestAnimationFrame().then(() => {
let activeDate
const type = getType(this.data.value)
if (type === 'array') {
activeDate = this.data.value[0]
} else if (type === 'number') {
activeDate = this.data.value
}
if (!activeDate) {
activeDate = Date.now()
}
const years = getYears(this.data.minDate, this.data.maxDate)
years.some((year, index) => {
if (compareYear(year, activeDate) === 0) {
this.setData({
scrollIntoView: `year${index}`
})
return true
}
return false
})
})
},
handleDateChange ({ detail: { value } }) {
this.$emit('change', {
value
})
}
}
})

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"year": "../year/index"
}
}

View File

@ -0,0 +1,25 @@
<jds src="../../utils.jds" module="utils" />
<view class="wd-year-panel">
<view v-if="{{ showPanelTitle }}" class="wd-year-panel__title">{{ title }}</view>
<scroll-view class="wd-year-panel__container" style="height: {{ (panelHeight || 378) + (showPanelTitle ? 26 : 16) }}px" scroll-y scroll-into-view="{{ scrollIntoView }}">
<year
jd:for="{{ utils.getYears(minDate, maxDate) }}"
jd:key="*this"
id="year{{index}}"
class="year"
type="{{ type }}"
date="{{ item }}"
data-date="{{ item }}"
value="{{ value }}"
min-date="{{ minDate }}"
max-date="{{ maxDate }}"
max-range="{{ maxRange }}"
formatter="{{ formatter }}"
range-prompt="{{ rangePrompt }}"
allow-same-day="{{ allowSameDay }}"
default-time="{{ defaultTime }}"
bind:change="handleDateChange"
/>
</scroll-view>
</view>

View File

@ -0,0 +1,15 @@
@import '../../../common/abstracts/variable';
@import '../../../common/abstracts/mixin';
@include b(year-panel) {
font-size: $-calendar-fs;
padding: $-calendar-panel-padding;
@include e(title) {
padding: 5px 0;
text-align: center;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
box-shadow: 0px 4px 8px 0 rgba(0, 0, 0, 0.02);
}
}

View File

@ -0,0 +1,79 @@
import VueComponent from '../common/component'
import { getDefaultTime } from './utils'
const current = new Date()
const currentYear = current.getFullYear()
const currentMonth = current.getMonth()
const currentDay = current.getDate()
VueComponent({
behaviors: ['jd://form-field'],
props: {
value: {
type: [null, Number, Array]
},
type: {
type: String,
value: 'date'
},
minDate: {
type: Number,
value: new Date(currentYear, currentMonth - 6, currentDay).getTime()
},
maxDate: {
type: Number,
value: new Date(currentYear, currentMonth + 6, currentDay, 23, 59, 59).getTime()
},
firstDayOfWeek: {
type: Number,
value: 0
},
formatter: null,
maxRange: Number,
rangePrompt: String,
allowSameDay: Boolean,
showPanelTitle: {
type: Boolean,
value: true
},
defaultTime: {
type: [String, Array],
observer (val) {
this.setData({
formatDefauleTime: getDefaultTime(val)
})
}
},
panelHeight: Number,
timeFilter: null,
hideSecond: Boolean
},
data: {
formatDefauleTime: []
},
methods: {
// 对外暴露方法
scrollIntoView (thresholds) {
const panel = this.getPanel()
panel.initRect && panel.initRect(thresholds)
panel.scrollIntoView()
},
getPanel () {
return this.data.type.indexOf('month') > -1 ? this.selectComponent('#yearPanel') : this.selectComponent('#monthPanel')
},
handleChange ({ detail: { value } }) {
this.setData({
value
})
this.$emit('change', {
value
})
},
handlePickStart () {
this.$emit('pickstart')
},
handlePickEnd () {
this.$emit('pickend')
}
}
})

View File

@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"wd-icon": "../icon/index",
"year-panel": "./components/yearPanel/index",
"month-panel": "./components/monthPanel/index"
}
}

View File

@ -0,0 +1,39 @@
<view class="wd-calendar-view custom-class">
<year-panel
v-if="{{ type === 'month' || type === 'monthrange' }}"
id="yearPanel"
type="{{ type }}"
value="{{ value }}"
min-date="{{ minDate }}"
max-date="{{ maxDate }}"
formatter="{{ formatter }}"
max-range="{{ maxRange }}"
range-prompt="{{ rangePrompt }}"
allow-same-day="{{ allowSameDay }}"
show-panel-title="{{ showPanelTitle }}"
default-time="{{ formatDefauleTime }}"
panel-height="{{ panelHeight }}"
bind:change="handleChange"
/>
<month-panel
v-else
id="monthPanel"
type="{{ type }}"
value="{{ value }}"
min-date="{{ minDate }}"
max-date="{{ maxDate }}"
first-day-of-week="{{ firstDayOfWeek }}"
formatter="{{ formatter }}"
max-range="{{ maxRange }}"
range-prompt="{{ rangePrompt }}"
allow-same-day="{{ allowSameDay }}"
show-panel-title="{{ showPanelTitle }}"
default-time="{{ formatDefauleTime }}"
panel-height="{{ panelHeight }}"
time-filter="{{ timeFilter }}"
hide-second="{{ hideSecond }}"
bind:change="handleChange"
bind:pickstart="handlePickStart"
bind:pickend="handlePickEnd"
/>
</view>

View File

@ -0,0 +1,176 @@
var weeks = ['日', '一', '二', '三', '四', '五', '六']
/**
* 比较两个日期的月份是否相等
* @param {timestamp} date1
* @param {timestamp} date2
*/
function compareMonth (date1, date2) {
date1 = getDate(date1)
date2 = getDate(date2)
var year1 = date1.getFullYear()
var year2 = date2.getFullYear()
var month1 = date1.getMonth()
var month2 = date2.getMonth()
if (year1 === year2) {
return month1 === month2 ? 0 : (month1 > month2 ? 1 : -1)
}
return year1 > year2 ? 1 : -1
}
/**
* 比较两个日期的年份是否相等
* @param {timestamp} date1
* @param {timestamp} date2
*/
function compareYear (date1, date2) {
date1 = getDate(date1)
date2 = getDate(date2)
var year1 = date1.getFullYear()
var year2 = date2.getFullYear()
return year1 === year2 ? 0 : (year1 > year2 ? 1 : -1)
}
function compareDay (date1, date2) {
date1 = getDate(date1)
date2 = getDate(date2)
var year1 = date1.getFullYear()
var year2 = date2.getFullYear()
var month1 = date1.getMonth()
var month2 = date2.getMonth()
var day1 = date1.getDate()
var day2 = date2.getDate()
if (year1 === year2) {
if (month1 === month2) {
return day1 === day2 ? 0 : (day1 > day2 ? 1 : -1)
}
return month1 > month2 ? 1 : -1
}
return year1 > year2 ? 1 : -1
}
/**
* 根据最小日期和最大日期获取这之间总共有几个月份
* @param {timestamp} minDate
* @param {timestamp} maxDate
*/
function getMonths (minDate, maxDate) {
var months = []
var month = getDate(minDate)
month.setDate(1)
while (compareMonth(month, maxDate) < 1) {
months.push(month.getTime())
month.setMonth(month.getMonth() + 1)
}
return months
}
function getYears (minDate, maxDate) {
var years = []
var year = getDate(minDate)
year.setMonth(0)
year.setDate(1)
while (compareYear(year, maxDate) < 1) {
years.push(year.getTime())
year.setFullYear(year.getFullYear() + 1)
}
return years
}
/**
* 根据下标获取星期
* @param {number} index
*/
function getWeekLabel (index) {
if (index >= 7) {
index = index % 7
}
return weeks[index]
}
/**
* 格式化年月
* @param {timestamp} date
*/
function formatMonthTitle (date) {
date = getDate(date)
var year = date.getFullYear()
var month = date.getMonth() + 1
return year + '年' + month + '月'
}
/**
* 格式化年
* @param {timestamp} date
*/
function formatYearTitle (date) {
date = getDate(date)
var year = date.getFullYear()
return year + '年'
}
/**
* 获取一个月第一天的样式
* @param {number} index
* @param {timestamp} date
* @param {number} firstDayOfWeek
*/
function getFirstDayStyle (index, date, firstDayOfWeek) {
if (firstDayOfWeek >= 7) {
firstDayOfWeek = firstDayOfWeek % 7
}
if (index !== 0) return ''
date = getDate(date)
var offset = (7 + date.getDay() - firstDayOfWeek) % 7
return 'margin-left: ' + (100 / 7) * offset + '%'
}
function getItemClass (monthType, value, type) {
var classList = [('is-' + monthType)]
if (type.indexOf('range') > -1) {
if (!value || !value[1]) {
classList.push('is-without-end')
}
}
return classList.join(' ')
}
/**
* 判断是否是范围选择
* @param {string} type
*/
function isRange (type) {
return type.indexOf('range') > -1
}
module.exports = {
getMonths: getMonths,
getWeekLabel: getWeekLabel,
getFirstDayStyle: getFirstDayStyle,
formatMonthTitle: formatMonthTitle,
getYears: getYears,
formatYearTitle: formatYearTitle,
getItemClass: getItemClass,
isRange: isRange
}

View File

@ -0,0 +1,374 @@
import { getType, padZero } from '../common/util'
/**
* 比较两个时间的日期是否相等
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function compareDate (date1, date2) {
date1 = new Date(date1)
date2 = new Date(date2)
const year1 = date1.getFullYear()
const year2 = date2.getFullYear()
const month1 = date1.getMonth()
const month2 = date2.getMonth()
const day1 = date1.getDate()
const day2 = date2.getDate()
if (year1 === year2) {
if (month1 === month2) {
return day1 === day2 ? 0 : (day1 > day2 ? 1 : -1)
}
return month1 === month2 ? 0 : (month1 > month2 ? 1 : -1)
}
return year1 > year2 ? 1 : -1
}
/**
* 比较两个日期的月份是否相等
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function compareMonth (date1, date2) {
date1 = new Date(date1)
date2 = new Date(date2)
const year1 = date1.getFullYear()
const year2 = date2.getFullYear()
const month1 = date1.getMonth()
const month2 = date2.getMonth()
if (year1 === year2) {
return month1 === month2 ? 0 : (month1 > month2 ? 1 : -1)
}
return year1 > year2 ? 1 : -1
}
/**
* 比较两个日期的年份是否一致
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function compareYear (date1, date2) {
date1 = new Date(date1)
date2 = new Date(date2)
const year1 = date1.getFullYear()
const year2 = date2.getFullYear()
return year1 === year2 ? 0 : (year1 > year2 ? 1 : -1)
}
/**
* 获取一个月的最后一天
* @param {number} year
* @param {number} month
*/
export function getMonthEndDay (year, month) {
return 32 - new Date(year, month - 1, 32).getDate()
}
/**
* 格式化年月
* @param {timestamp} date
*/
export function formatMonthTitle (date) {
date = new Date(date)
const year = date.getFullYear()
const month = date.getMonth() + 1
return year + '年' + month + '月'
}
/**
* 格式化年份
* @param {timestamp} date
*/
export function formatYearTitle (date) {
date = new Date(date)
const year = date.getFullYear()
return year + '年'
}
/**
* 根据最小日期和最大日期获取这之间总共有几个月份
* @param {timestamp} minDate
* @param {timestamp} maxDate
*/
export function getMonths (minDate, maxDate) {
const months = []
const month = new Date(minDate)
month.setDate(1)
while (compareMonth(month, maxDate) < 1) {
months.push(month.getTime())
month.setMonth(month.getMonth() + 1)
}
return months
}
/**
* 根据最小日期和最大日期获取这之间总共有几年
* @param {timestamp} minDate
* @param {timestamp} maxDate
*/
export function getYears (minDate, maxDate) {
const years = []
const year = new Date(minDate)
year.setMonth(0)
year.setDate(1)
while (compareYear(year, maxDate) < 1) {
years.push(year.getTime())
year.setFullYear(year.getFullYear() + 1)
}
return years
}
/**
* 获取一个日期所在周的第一天和最后一天
* @param {timestamp} date
*/
export function getWeekRange (date, firstDayOfWeek) {
if (firstDayOfWeek >= 7) {
firstDayOfWeek = firstDayOfWeek % 7
}
date = new Date(date)
date.setHours(0, 0, 0, 0)
const year = date.getFullYear()
const month = date.getMonth()
const day = date.getDate()
const week = date.getDay()
const weekStart = new Date(year, month, day - (7 + week - firstDayOfWeek) % 7)
const weekEnd = new Date(year, month, day + 6 - (7 + week - firstDayOfWeek) % 7)
return [weekStart.getTime(), weekEnd.getTime()]
}
/**
* 获取日期偏移量
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function getDayOffset (date1, date2) {
return (date1 - date2) / (24 * 60 * 60 * 1000) + 1
}
/**
* 获取偏移日期
* @param {timestamp} date
* @param {number} offset
*/
export function getDayByOffset (date, offset) {
date = new Date(date)
date.setDate(date.getDate() + offset)
return date.getTime()
}
/**
* 获取月份偏移量
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function getMonthOffset (date1, date2) {
date1 = new Date(date1)
date2 = new Date(date2)
const year1 = date1.getFullYear()
const year2 = date2.getFullYear()
let month1 = date1.getMonth()
const month2 = date2.getMonth()
month1 = (year1 - year2) * 12 + month1
return month1 - month2 + 1
}
/**
* 获取偏移月份
* @param {timestamp} date
* @param {number} offset
*/
export function getMonthByOffset (date, offset) {
date = new Date(date)
date.setMonth(date.getMonth() + offset)
return date.getTime()
}
/**
* 获取默认时间格式化为数组
* @param {array|string|null} defaultTime
*/
export function getDefaultTime (defaultTime) {
if (getType(defaultTime) === 'array') {
const startTime = (defaultTime[0] || '00:00:00').split(':').map(item => {
return parseInt(item)
})
const endTime = (defaultTime[1] || '00:00:00').split(':').map(item => {
return parseInt(item)
})
return [startTime, endTime]
} else {
const time = (defaultTime || '00:00:00').split(':').map(item => {
return parseInt(item)
})
return [time, time]
}
}
/**
* 根据默认时间获取日期
* @param {timestamp} date
* @param {array} defaultTime
*/
export function getDateByDefaultTime (date, defaultTime) {
date = new Date(date)
date.setHours(defaultTime[0])
date.setMinutes(defaultTime[1])
date.setSeconds(defaultTime[2])
return date.getTime()
}
/**
* 获取经过 iteratee 格式化后的长度为 n 的数组
* @param {number} n
* @param {function} iteratee
*/
const times = (n, iteratee) => {
let index = -1
const result = Array(n < 0 ? 0 : n)
while (++index < n) {
result[index] = iteratee(index)
}
return result
}
/**
* 获取时分秒
* @param {timestamp}} date
*/
const getTime = (date) => {
date = new Date(date)
return [date.getHours(), date.getMinutes(), date.getSeconds()]
}
/**
* 根据最小最大日期获取时间数据用于填入picker
* @param {*} param0
*/
export function getTimeData ({ date, minDate, maxDate, isHideSecond, filter } = {}) {
const compareMin = compareDate(date, minDate)
const compareMax = compareDate(date, maxDate)
let minHour = 0
let maxHour = 23
let minMinute = 0
let maxMinute = 59
let minSecond = 0
let maxSecond = 59
if (compareMin === 0) {
const minTime = getTime(minDate)
const currentTime = getTime(date)
minHour = minTime[0]
if (minTime[0] === currentTime[0]) {
minMinute = minTime[1]
if (minTime[1] === currentTime[1]) {
minSecond = minTime[2]
}
}
}
if (compareMax === 0) {
const maxTime = getTime(maxDate)
const currentTime = getTime(date)
maxHour = maxTime[0]
if (maxTime[0] === currentTime[0]) {
maxMinute = maxTime[1]
if (maxTime[1] === currentTime[1]) {
maxSecond = maxTime[2]
}
}
}
let columns = []
let hours = times(24, index => {
return {
label: `${padZero(index)}`,
value: index,
disabled: index < minHour || index > maxHour
}
})
let minutes = times(60, index => {
return {
label: `${padZero(index)}`,
value: index,
disabled: index < minMinute || index > maxMinute
}
})
let seconds
if (filter && getType(filter) === 'function') {
hours = filter({
type: 'hour',
values: hours
})
minutes = filter({
type: 'minute',
values: minutes
})
}
if (!isHideSecond) {
seconds = times(60, index => {
return {
label: `${padZero(index)}`,
value: index,
disabled: index < minSecond || index > maxSecond
}
})
if (filter && getType(filter) === 'function') {
seconds = filter({
type: 'second',
values: seconds
})
}
}
columns = isHideSecond ? [hours, minutes] : [hours, minutes, seconds]
return columns
}
/**
* 获取当前是第几周
* @param {timestamp} date
*/
export function getWeekNumber (date) {
date = new Date(date)
date.setHours(0, 0, 0, 0)
// Thursday in current week decides the year.
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7)
// January 4 is always in week 1.
const week = new Date(date.getFullYear(), 0, 4)
// Adjust to Thursday in week 1 and count number of weeks from date to week 1.
// Rounding should be fine for Daylight Saving Time. Its shift should never be more than 12 hours.
return 1 + Math.round(((date.getTime() - week.getTime()) / 86400000 - 3 + (week.getDay() + 6) % 7) / 7)
}

View File

@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,377 @@
import VueComponent from '../common/component'
import { isEqual, padZero } from '../common/util'
import { getDefaultTime, getWeekNumber } from '../calendarView/utils'
import cell from '../mixins/cell'
import dayjs from '../common/dayjs.min'
const current = new Date()
const currentYear = current.getFullYear()
const currentMonth = current.getMonth()
const currentDay = current.getDate()
const defaultDisplayFormat = (value, type) => {
switch (type) {
case 'date':
return dayjs(value).format('YYYY-MM-DD')
case 'dates':
return value
.map((item) => {
return dayjs(item).format('YYYY-MM-DD')
})
.join(', ')
case 'daterange':
return `${value[0] ? dayjs(value[0]).format('YYYY-MM-DD') : '开始时间'}${value[1] ? dayjs(value[1]).format('YYYY-MM-DD') : '结束时间'}`
case 'datetime':
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
case 'datetimerange':
return `${value[0] ? dayjs(value[0]).format('YY年MM月DD日 HH:mm:ss') : '开始时间'}\n${
value[1] ? dayjs(value[1]).format('YY年MM月DD日 HH:mm:ss') : '结束时间'
}`
case 'week': {
const year = new Date(value).getFullYear()
const week = getWeekNumber(value)
return `${year}${padZero(week)}`
}
case 'weekrange': {
const year1 = new Date(value[0]).getFullYear()
const week1 = getWeekNumber(value[0])
const year2 = new Date(value[1]).getFullYear()
const week2 = getWeekNumber(value[1])
return `${value[0] ? `${year1}${padZero(week1)}` : '开始周'} - ${value[1] ? `${year2}${padZero(week2)}` : '结束周'}`
}
case 'month':
return dayjs(value).format('YYYY / MM')
case 'monthrange':
return `${value[0] ? dayjs(value[0]).format('YYYY / MM') : '开始月'}${value[1] ? dayjs(value[1]).format('YYYY / MM') : '结束月'}`
}
}
const formatRange = (value, rangeType, type) => {
switch (type) {
case 'daterange':
if (!value) {
return rangeType === 'end' ? '结束时间' : '开始时间'
}
return dayjs(value).format('YYYY年MM月DD日')
case 'datetimerange':
if (!value) {
return rangeType === 'end' ? '结束时间' : '开始时间'
}
return dayjs(value).format('YY年MM月DD日 HH:mm:ss')
case 'weekrange': {
if (!value) {
return rangeType === 'end' ? '结束周' : '开始周'
}
const date = new Date(value)
const year = date.getFullYear()
const week = getWeekNumber(value)
return year + '年第' + week + '周'
}
case 'monthrange':
if (!value) {
return rangeType === 'end' ? '结束月' : '开始月'
}
return dayjs(value).format('YYYY年MM月')
}
}
VueComponent({
externalClasses: ['custom-view-class', 'custom-label-class', 'custom-value-class'],
behaviors: [cell, 'jd://form-field'],
relations: {
'../cellGroup/index': {
type: 'ancestor',
linked(target) {
this.parent = target
},
unlinked() {
this.parent = null
}
}
},
props: {
value: {
type: [null, Number, Array],
observer(val, oldVal) {
if (isEqual(val, oldVal)) return
this.setData(
{
calendarValue: val,
confirmBtnDisabled: this.getConfirmBtnStatus(val)
},
() => {
this.scrollIntoView()
}
)
this.setShowValue()
if (this.data.type.indexOf('range') > -1) {
this.setInnerLabel()
}
}
},
type: {
type: String,
value: 'date',
observer(val) {
if (this.data.showTypeSwitch) {
const tabs = ['date', 'week', 'month']
const rangeTabs = ['daterange', 'weekrange', 'monthrange']
const index = val.indexOf('range') > -1 ? rangeTabs.indexOf(val) || 0 : tabs.indexOf(val)
this.setData({
currentTab: index
})
}
this.setData({
panelHeight: this.data.showConfirm ? 338 : 400,
currentType: val
})
if (this.data.type.indexOf('range') > -1) {
this.setInnerLabel()
}
}
},
minDate: {
type: Number,
value: new Date(currentYear, currentMonth - 6, currentDay).getTime()
},
maxDate: {
type: Number,
value: new Date(currentYear, currentMonth + 6, currentDay, 23, 59, 59).getTime()
},
firstDayOfWeek: {
type: Number,
value: 0
},
formatter: null,
maxRange: Number,
rangePrompt: String,
allowSameDay: Boolean,
defaultTime: {
type: [String, Array],
observer(val) {
this.setData({
formatDefauleTime: getDefaultTime(val)
})
}
},
timeFilter: null,
hideSecond: Boolean,
label: String,
labelWidth: String,
useLabelSlot: Boolean,
useDefaultSlot: Boolean,
disabled: Boolean,
readonly: Boolean,
placeholder: String,
title: String,
alignRight: Boolean,
error: Boolean,
required: Boolean,
size: String,
center: Boolean,
closeOnClickModal: {
type: Boolean,
value: true
},
zIndex: {
type: Number,
value: 15
},
showConfirm: {
type: Boolean,
value: true,
observer(val) {
this.setData({
panelHeight: val ? 338 : 400
})
}
},
confirmText: String,
displayFormat: null,
innerDisplayFormat: null,
ellipsis: Boolean,
showTypeSwitch: Boolean,
shortcuts: Array,
onShortcutsClick: null,
safeAreaInsetBottom: {
type: Boolean,
value: true
},
beforeConfirm: null
},
data: {
pickerShow: false,
calendarValue: '',
lastCalendarValue: '',
panelHeight: 338,
confirmBtnDisabled: true,
showValue: '',
currentTab: 0,
lastTab: 0,
currentType: 'date',
lastCurrentType: 'date',
inited: false,
rangeLabel: []
},
methods: {
scrollIntoView() {
const calendarView = this.selectComponent('#calendarView')
calendarView && calendarView.scrollIntoView()
},
// 对外暴露方法
open() {
const { disabled, readonly } = this.data
if (disabled || readonly) return
this.setData(
{
inited: true,
pickerShow: true,
lastCalendarValue: this.data.calendarValue,
lastTab: this.data.currentTab,
lastCurrentType: this.data.currentType
},
() => {
setTimeout(() => {
this.scrollIntoView()
if (this.data.showTypeSwitch) {
const tab = this.selectComponent('#tabs')
tab.scrollIntoView()
tab.updateLineStyle(false)
}
}, 250)
}
)
},
// 对外暴露方法
close() {
this.setData({
pickerShow: false
})
setTimeout(() => {
const confirmBtnDisabled = this.getConfirmBtnStatus(this.data.lastCalendarValue)
this.setData({
calendarValue: this.data.lastCalendarValue,
currentTab: this.data.lastTab,
currentType: this.data.lastCurrentType,
confirmBtnDisabled
})
}, 250)
this.$emit('cancel')
},
handleTypeChange({ detail: { index } }) {
const tabs = ['date', 'week', 'month']
const rangeTabs = ['daterange', 'weekrange', 'monthrange']
const type = this.data.type.indexOf('range') > -1 ? rangeTabs[index] : tabs[index]
this.setData({
currentTab: index,
currentType: type
})
},
getConfirmBtnStatus(value) {
let confirmBtnDisabled = false
// 范围选择未选择满,或者多日期选择未选择日期,按钮置灰不可点击
if (
(this.data.type.indexOf('range') > -1 && (!value[0] || !value[1] || !value)) ||
(this.data.type === 'dates' && (value.length === 0 || !value)) ||
!value
) {
confirmBtnDisabled = true
}
return confirmBtnDisabled
},
handleChange({ detail: { value } }) {
const confirmBtnDisabled = this.getConfirmBtnStatus(value)
this.setData({
calendarValue: value,
confirmBtnDisabled
})
this.$emit('change', {
value
})
if (this.data.type.indexOf('range') > -1) {
this.setInnerLabel()
}
if (!this.data.showConfirm && !confirmBtnDisabled) {
this.handleConfirm()
}
},
handleConfirm() {
if (this.data.beforeConfirm) {
this.data.beforeConfirm({
value: this.data.calendarValue,
resolve: (isPass) => {
isPass && this.onConfirm()
}
})
} else {
this.onConfirm()
}
},
onConfirm() {
this.setData({
pickerShow: false,
value: this.data.calendarValue
})
this.$emit('confirm', {
value: this.data.calendarValue
})
this.setShowValue()
},
setInnerLabel() {
const [start, end] = this.data.calendarValue || []
this.setData({
rangeLabel: [start, end].map((item, index) => {
return (this.data.innerDisplayFormat || formatRange)(item, index === 0 ? 'start' : 'end', this.data.currentType)
})
})
},
setShowValue() {
if (
(!(this.data.calendarValue instanceof Array) && this.data.calendarValue) ||
(this.data.calendarValue instanceof Array && this.data.calendarValue.length)
) {
this.setData({
showValue: (this.data.displayFormat || defaultDisplayFormat)(this.data.calendarValue, this.data.currentType)
})
} else {
this.setData({
showValue: ''
})
}
},
handleShortcutClick(event) {
const { index } = event.target.dataset
if (this.data.onShortcutsClick && typeof this.data.onShortcutsClick === 'function') {
const calendarValue = this.data.onShortcutsClick({
item: this.data.shortcuts[index],
index
})
const confirmBtnDisabled = this.getConfirmBtnStatus(calendarValue)
this.setData({
calendarValue,
confirmBtnDisabled
})
}
if (!this.data.showConfirm) {
this.handleConfirm()
}
}
}
})

View File

@ -0,0 +1,12 @@
{
"component": true,
"usingComponents": {
"wd-action-sheet": "../actionSheet/index",
"wd-icon": "../icon/index",
"wd-calendar-view": "../calendarView/index",
"wd-button": "../button/index",
"wd-tabs": "../tabs/index",
"wd-tab": "../tab/index",
"wd-tag": "../tag/index"
}
}

View File

@ -0,0 +1,87 @@
<jds src="../calendarView/utils.jds" module="utils" />
<view class="wd-calendar {{ border ? 'is-border' : '' }} custom-class">
<view class="wd-calendar__field" bind:tap="open">
<slot v-if="{{ useDefaultSlot }}"></slot>
<view v-else class="wd-calendar__cell {{ disabled && 'is-disabled' }} {{ readonly && 'is-readonly' }} {{ alignRight && 'is-align-right' }} {{ error && 'is-error' }} {{ size && ('is-' + size) }} {{ center && 'is-center' }}">
<view
v-if="{{ label || useLabelSlot }}"
class="wd-calendar__label {{ required && 'is-required' }} custom-label-class"
style="{{ labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : '' }}"
>
<block v-if="{{ label }}">{{ label }}</block>
<slot v-else name="label"></slot>
</view>
<view class="wd-calendar__value {{ ellipsis && 'is-ellipsis' }} custom-value-class {{ showValue ? '' : 'wd-calendar__value--placeholder' }}">{{ showValue || placeholder || '请选择' }}</view>
<wd-icon
v-if="{{!disabled && !readonly}}"
custom-class="wd-calendar__arrow"
name="arrow-right"
/>
</view>
</view>
<wd-action-sheet
show="{{ pickerShow }}"
duration="{{ 250 }}"
close-on-click-modal="{{ closeOnClickModal }}"
safe-area-inset-bottom="{{ safeAreaInsetBottom }}"
z-index="{{ zIndex }}"
bind:close="close"
>
<view class="wd-calendar__header">
<view v-if="{{ !showTypeSwitch && shortcuts.length === 0 }}" class="wd-calendar__title">{{ title || '选择日期' }}</view>
<view v-if="{{ showTypeSwitch }}" class="wd-calendar__tabs">
<wd-tabs id="tabs" value="{{ currentTab }}" bind:change="handleTypeChange">
<wd-tab title="日" />
<wd-tab title="周" />
<wd-tab title="月" />
</wd-tabs>
</view>
<view v-if="{{ shortcuts.length > 0 }}" class="wd-calendar__shortcuts">
<wd-tag
jd:for="{{ shortcuts }}"
jd:key="index"
custom-class="wd-calendar__tag"
type="primary"
plain
round
data-index="{{ index }}"
bind:click="handleShortcutClick"
>{{ item.text }}</wd-tag>
</view>
<wd-icon custom-class="wd-calendar__close" name="add" bind:tap="close"/>
</view>
<view v-if="{{ inited }}" class="wd-calendar__view {{ currentType.indexOf('range') > -1 && 'is-range' }} {{ showConfirm && 'is-show-confirm' }}">
<view v-if="{{ utils.isRange(type) }}" class="wd-calendar__range-label {{ type === 'monthrange' && 'is-monthrange' }}">
<view class="wd-calendar__range-label-item {{ (!calendarValue || !calendarValue[0]) && 'is-placeholder' }}" style="text-align: right;">
{{ rangeLabel[0] }}
</view>
<view class="wd-calendar__range-sperator">/</view>
<view class="wd-calendar__range-label-item {{ (!calendarValue || !calendarValue[1]) && 'is-placeholder' }}">
{{ rangeLabel[1] }}
</view>
</view>
<wd-calendar-view
id="calendarView"
value="{{ calendarValue }}"
type="{{ currentType }}"
min-date="{{ minDate }}"
max-date="{{ maxDate }}"
first-day-of-week="{{ firstDayOfWeek }}"
formatter="{{ formatter }}"
panel-height="{{ panelHeight }}"
max-range="{{ maxRange }}"
range-prompt="{{ rangePrompt }}"
allow-same-day="{{ allowSameDay }}"
default-time="{{ defaultTime }}"
time-filter="{{ timeFilter }}"
hide-second="{{ hideSecond }}"
show-panel-title="{{ !utils.isRange(type) }}"
bind:change="handleChange"
/>
</view>
<view v-if="{{ showConfirm }}" class="wd-calendar__confirm">
<wd-button block disabled="{{ confirmBtnDisabled }}" bind:click="handleConfirm">{{ confirmText || '确定' }}</wd-button>
</view>
</wd-action-sheet>
</view>

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