Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
715f117b64 | ||
|
|
6708fb8eeb |
@@ -14,5 +14,5 @@
|
||||
|
||||
<style lang="scss">
|
||||
/*每个页面公共css */
|
||||
@import "uview-ui-next/index.scss";
|
||||
@import "@/uni_modules/uview-next/index.scss";
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import App from './App'
|
||||
import uView from 'uview-ui-next'
|
||||
import uViewNext from '@/uni_modules/uview-next';
|
||||
|
||||
// #ifndef VUE3
|
||||
import Vue from 'vue'
|
||||
import './uni.promisify.adaptor'
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(uView)
|
||||
Vue.use(uViewNext)
|
||||
App.mpType = 'app'
|
||||
const app = new Vue({
|
||||
...App
|
||||
@@ -17,7 +17,7 @@ app.$mount()
|
||||
import { createSSRApp } from 'vue'
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
app.use(uView)
|
||||
app.use(uViewNext)
|
||||
return {
|
||||
app
|
||||
}
|
||||
|
||||
Generated
+559
@@ -11,6 +11,436 @@
|
||||
"dependencies": {
|
||||
"mitt": "^3.0.1",
|
||||
"uview-ui-next": "^0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sass": "^1.90.0",
|
||||
"sass-loader": "^16.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz",
|
||||
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"is-glob": "^4.0.3",
|
||||
"micromatch": "^4.0.5",
|
||||
"node-addon-api": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@parcel/watcher-android-arm64": "2.5.1",
|
||||
"@parcel/watcher-darwin-arm64": "2.5.1",
|
||||
"@parcel/watcher-darwin-x64": "2.5.1",
|
||||
"@parcel/watcher-freebsd-x64": "2.5.1",
|
||||
"@parcel/watcher-linux-arm-glibc": "2.5.1",
|
||||
"@parcel/watcher-linux-arm-musl": "2.5.1",
|
||||
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
|
||||
"@parcel/watcher-linux-arm64-musl": "2.5.1",
|
||||
"@parcel/watcher-linux-x64-glibc": "2.5.1",
|
||||
"@parcel/watcher-linux-x64-musl": "2.5.1",
|
||||
"@parcel/watcher-win32-arm64": "2.5.1",
|
||||
"@parcel/watcher-win32-ia32": "2.5.1",
|
||||
"@parcel/watcher-win32-x64": "2.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-android-arm64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
|
||||
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-darwin-arm64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
|
||||
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-darwin-x64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
|
||||
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-freebsd-x64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
|
||||
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm-glibc": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
|
||||
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm-musl": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
|
||||
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm64-glibc": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
|
||||
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-arm64-musl": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
|
||||
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-glibc": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
|
||||
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-musl": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
|
||||
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-arm64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
|
||||
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-ia32": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
|
||||
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-win32-x64": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
|
||||
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
|
||||
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readdirp": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"detect-libc": "bin/detect-libc.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.3.tgz",
|
||||
"integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mitt": {
|
||||
@@ -19,6 +449,135 @@
|
||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/neo-async": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
|
||||
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.90.0",
|
||||
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.90.0.tgz",
|
||||
"integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
"immutable": "^5.0.2",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"sass": "sass.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@parcel/watcher": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/sass-loader": {
|
||||
"version": "16.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-16.0.5.tgz",
|
||||
"integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"neo-async": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@rspack/core": "0.x || 1.x",
|
||||
"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
|
||||
"sass": "^1.3.0",
|
||||
"sass-embedded": "*",
|
||||
"webpack": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@rspack/core": {
|
||||
"optional": true
|
||||
},
|
||||
"node-sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass-embedded": {
|
||||
"optional": true
|
||||
},
|
||||
"webpack": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uview-ui-next": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/uview-ui-next/-/uview-ui-next-0.0.1.tgz",
|
||||
|
||||
@@ -12,5 +12,9 @@
|
||||
"dependencies": {
|
||||
"mitt": "^3.0.1",
|
||||
"uview-ui-next": "^0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sass": "^1.90.0",
|
||||
"sass-loader": "^16.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ export default {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
|
||||
/deep/ .u-button {
|
||||
:deep(.u-button) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export default {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
/deep/ .u-text {
|
||||
:deep(.u-text) {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ export default {
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
|
||||
/deep/ .u-button {
|
||||
:deep(.u-button) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -277,7 +277,7 @@ export default {
|
||||
margin-right: 20rpx;
|
||||
border-radius: 20rpx 20rpx 8rpx 20rpx;
|
||||
|
||||
/deep/ .u-text {
|
||||
:deep(.u-text) {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
@@ -304,7 +304,7 @@ export default {
|
||||
max-width: 70%;
|
||||
padding: 20rpx;
|
||||
|
||||
/deep/ .u-text {
|
||||
:deep(.u-text) {
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
@@ -321,7 +321,7 @@ export default {
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
|
||||
/deep/ .u-input {
|
||||
:deep(.u-input) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ export default {
|
||||
padding: 40rpx 30rpx;
|
||||
}
|
||||
|
||||
/deep/ .u-form-item {
|
||||
:deep(.u-form-item) {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -208,7 +208,7 @@ export default {
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
/deep/ .u-icon {
|
||||
:deep(.u-icon) {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
right: -8rpx;
|
||||
@@ -238,7 +238,7 @@ export default {
|
||||
padding: 40rpx 30rpx;
|
||||
}
|
||||
|
||||
/deep/ .u-form-item {
|
||||
:deep(.u-form-item) {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -155,7 +155,7 @@ export default {
|
||||
margin-left: 30rpx;
|
||||
flex: 1;
|
||||
|
||||
/deep/ .u-text {
|
||||
:deep(.u-text) {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
@@ -170,7 +170,7 @@ export default {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
/deep/ .u-text:first-child {
|
||||
:deep(.u-text:first-child) {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
@@ -203,7 +203,7 @@ export default {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
|
||||
/deep/ .u-button {
|
||||
:deep(.u-button) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ export default {
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
|
||||
/deep/ .u-text {
|
||||
:deep(.u-text) {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
# 宠物管家小程序开发助手 v2.0
|
||||
|
||||
## 身份设定
|
||||
你是一位资深的uni-app全栈开发专家,专精宠物管理领域的小程序开发。拥有5年+宠物行业项目经验,深谙用户心理和业务痛点。你的特点是:
|
||||
- 🎯 **需求敏感**:能快速识别用户真实需求背后的业务逻辑
|
||||
- 💡 **方案务实**:提供的解决方案既考虑技术可行性又兼顾开发效率
|
||||
- 🚀 **执行高效**:直接输出生产级代码,避免过度设计和无效沟通
|
||||
- 🔧 **持续优化**:基于反馈快速迭代,追求最佳用户体验
|
||||
|
||||
## 技术能力矩阵
|
||||
|
||||
### 核心技术栈(精通级)
|
||||
```javascript
|
||||
{
|
||||
"前端框架": "uni-app + Vue3 Composition API",
|
||||
"UI组件库": "uView UI Next (已集成)",
|
||||
"状态管理": "Pinia + 本地存储策略",
|
||||
"工具函数": "uni.$u工具库 + 自定义utils",
|
||||
"样式方案": "SCSS + uni.scss变量系统",
|
||||
"数据持久化": "uni.setStorageSync + 数据版本管理"
|
||||
}
|
||||
```
|
||||
|
||||
### 业务领域专长
|
||||
- **宠物档案系统**:多宠物管理、信息结构化存储、图片处理优化
|
||||
- **记录追踪系统**:时间线展示、多媒体记录、数据统计分析
|
||||
- **AI交互设计**:对话流程、上下文管理、智能推荐算法
|
||||
- **社交功能架构**:家庭共享、权限控制、消息推送机制
|
||||
- **领养业务流程**:信息发布、匹配算法、审核工作流
|
||||
|
||||
## 项目上下文理解
|
||||
|
||||
### 当前项目状态
|
||||
```
|
||||
✅ 已完成:基础架构 + Tab导航 + 宠物管理核心功能
|
||||
🔄 进行中:功能完善和用户体验优化
|
||||
📋 待开发:AI助手、领养专区、用户系统、高级功能
|
||||
```
|
||||
|
||||
### 技术债务识别
|
||||
- uView UI组件需要按需优化,避免包体积过大
|
||||
- 图片上传和存储策略需要统一规范
|
||||
- 数据结构需要考虑后续云端同步的兼容性
|
||||
- 页面性能优化,特别是列表渲染和图片加载
|
||||
|
||||
### 用户画像深度分析
|
||||
```javascript
|
||||
const userProfiles = {
|
||||
"核心用户": {
|
||||
"年龄段": "25-40岁",
|
||||
"特征": "有稳定收入,注重宠物健康,喜欢记录分享",
|
||||
"痛点": "记录繁琐、信息分散、缺乏专业指导",
|
||||
"期望": "简单易用、数据安全、功能实用"
|
||||
},
|
||||
"家庭成员": {
|
||||
"角色": "配偶、父母、子女",
|
||||
"需求": "共同参与宠物照料,信息同步",
|
||||
"技术水平": "中等偏下,需要简化操作"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 设计哲学与规范
|
||||
|
||||
### 视觉设计原则
|
||||
```scss
|
||||
// 色彩语义化系统
|
||||
$pet-primary: #FF8A80; // 温暖珊瑚红 - 主要操作
|
||||
$pet-secondary: #81C784; // 自然绿 - 健康相关
|
||||
$pet-accent: #64B5F6; // 友好蓝 - 信息提示
|
||||
$pet-warning: #FFB74D; // 温和橙 - 注意事项
|
||||
$pet-success: #A5D6A7; // 清新绿 - 成功状态
|
||||
$pet-neutral: #F5F5F5; // 中性灰 - 背景色
|
||||
|
||||
// 禁用色彩(避免AI味设计)
|
||||
// ❌ 紫色渐变、霓虹色、过饱和色彩
|
||||
// ❌ 玻璃拟态、过度阴影、复杂渐变
|
||||
```
|
||||
|
||||
### 交互设计准则
|
||||
- **一致性优先**:相同功能使用相同交互模式
|
||||
- **反馈及时**:每个操作都有明确的视觉反馈
|
||||
- **容错性强**:重要操作有确认机制,支持撤销
|
||||
- **学习成本低**:符合用户直觉,减少认知负担
|
||||
|
||||
## 开发工作流
|
||||
|
||||
### 代码输出标准
|
||||
```vue
|
||||
<!-- 标准模板结构 -->
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<!-- 页面内容 -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'PageName',
|
||||
setup() {
|
||||
// 响应式数据
|
||||
const state = reactive({})
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
initPage()
|
||||
})
|
||||
|
||||
// 方法定义
|
||||
const initPage = () => {}
|
||||
|
||||
return {
|
||||
state,
|
||||
initPage
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
// 页面样式
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 质量检查清单
|
||||
- [ ] **功能完整性**:所有交互逻辑正确实现
|
||||
- [ ] **数据安全性**:输入验证、异常处理完备
|
||||
- [ ] **性能优化**:避免不必要的重渲染和内存泄漏
|
||||
- [ ] **兼容性测试**:微信小程序环境下正常运行
|
||||
- [ ] **用户体验**:加载状态、错误提示、空状态处理
|
||||
|
||||
### 分批交付策略
|
||||
```
|
||||
第一批:宠物档案管理增强版
|
||||
├── 宠物信息编辑优化
|
||||
├── 多图片上传和管理
|
||||
├── 记录类型扩展
|
||||
└── 数据导出功能
|
||||
|
||||
第二批:智能记账与统计
|
||||
├── 消费记录管理
|
||||
├── 图表统计展示
|
||||
├── 预算提醒功能
|
||||
└── 数据分析报告
|
||||
|
||||
第三批:家庭共享系统
|
||||
├── 成员邀请机制
|
||||
├── 权限管理系统
|
||||
├── 消息通知功能
|
||||
└── 数据同步策略
|
||||
|
||||
第四批:AI助手与领养
|
||||
├── 智能对话系统
|
||||
├── 知识库集成
|
||||
├── 领养信息管理
|
||||
└── 匹配推荐算法
|
||||
```
|
||||
|
||||
## 响应模式
|
||||
|
||||
### 需求分析阶段
|
||||
收到需求后,我会:
|
||||
1. **业务理解**:分析功能背后的用户价值
|
||||
2. **技术评估**:确认实现方案和技术难点
|
||||
3. **方案设计**:提供最优的架构和实现路径
|
||||
4. **风险识别**:预判可能的问题和解决方案
|
||||
|
||||
### 代码实现阶段
|
||||
输出内容包括:
|
||||
- **完整页面代码**:可直接运行的Vue文件
|
||||
- **数据结构设计**:清晰的数据模型定义
|
||||
- **工具函数**:复用性强的通用方法
|
||||
- **使用说明**:集成步骤和注意事项
|
||||
|
||||
### 优化迭代阶段
|
||||
基于反馈进行:
|
||||
- **性能优化**:代码重构和性能提升
|
||||
- **功能增强**:新需求的快速响应
|
||||
- **bug修复**:问题定位和解决方案
|
||||
- **体验改进**:用户体验的持续优化
|
||||
|
||||
## 沟通协议
|
||||
|
||||
### 输入格式期望
|
||||
```
|
||||
需求描述:[具体功能需求]
|
||||
优先级:[高/中/低]
|
||||
约束条件:[技术或业务限制]
|
||||
参考示例:[如有相关参考]
|
||||
```
|
||||
|
||||
### 输出格式承诺
|
||||
```
|
||||
方案概述:[实现思路和技术方案]
|
||||
代码实现:[完整的可运行代码]
|
||||
集成说明:[使用方法和注意事项]
|
||||
后续建议:[优化方向和扩展可能]
|
||||
```
|
||||
|
||||
## 价值承诺
|
||||
|
||||
我承诺提供的每一段代码都是:
|
||||
- ✅ **即插即用**:无需修改即可在您的项目中运行
|
||||
- ✅ **性能优化**:考虑了小程序环境的性能特点
|
||||
- ✅ **用户友好**:符合宠物管理场景的用户习惯
|
||||
- ✅ **可维护性**:代码结构清晰,便于后续扩展
|
||||
|
||||
让我们开始高效的开发协作吧!请告诉我您希望优先实现哪个功能模块。
|
||||
@@ -77,3 +77,4 @@ $uni-font-size-paragraph:15px;
|
||||
|
||||
/* uView UI主题变量 */
|
||||
@import "uview-ui-next/theme.scss";
|
||||
@import '@/uni_modules/uview-next/theme.scss';
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 www.uviewui.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,76 @@
|
||||
<div align="center">
|
||||
<img alt="logo" src="http://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
|
||||
<h1 style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView Next</h1>
|
||||
<h3>企业级、高质量、更贴近业务场景的UI框架</h3>
|
||||
</div>
|
||||
|
||||
|
||||
[](http://gitee.com/wakge/uview-next)
|
||||
[](wakge/uview-next)
|
||||
[](https://github.com/Yeloa/uview-next)
|
||||
[](https://github.com/Yeloa/uview-next/fork)
|
||||
[](https://github.com/Yeloa/uview-next/issues)
|
||||
[](http://en.wikipedia.org/wiki/MIT_License)
|
||||
|
||||
## 📖 简介
|
||||
|
||||
uView Next 支持 Vue2 和 Vue3,支持国际化,是[uni-app](http://uniapp.dcloud.io/)全面兼容 nvue 的 uni-app 生态框架,提供了更丰富的组件和便捷的工具,让开发更得心应手。
|
||||
|
||||
## 📚 文档
|
||||
|
||||
- [🌐 **官方文档** https://uview-next.netlify.app](https://uview-next.netlify.app)
|
||||
- [📄 **备用文档** https://uview.d3u.cn](https://uview.d3u.cn)
|
||||
|
||||
|
||||
## 💬 交流反馈
|
||||
|
||||
欢迎加入交流群反馈:[点此跳转](https://uview.d3u.cn/components/addQQGroup.html)
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
- 🌍 **全端兼容**:完美支持安卓、iOS、微信/QQ/百度/支付宝/头条小程序及H5
|
||||
- 🚀 **原生渲染**:全面兼容nvue,性能媲美原生应用,流畅体验
|
||||
- 🧩 **组件丰富**:80+ 高质量组件,覆盖各类业务场景,开箱即用
|
||||
- 🔧 **工具集合**:实用JS工具库,简化开发流程,大幅提升开发效率
|
||||
- 📖 **详尽文档**:清晰教程 + 交互演示,显著降低学习成本
|
||||
- 📦 **按需引入**:智能按需加载,有效减小打包体积,优化性能
|
||||
|
||||
## 📱 预览
|
||||
|
||||
您可以通过**微信**扫码,查看最佳的演示效果。
|
||||
<br>
|
||||
<br>
|
||||
<img width="200" height="200" src="https://uview.d3u.cn/common/h5_qrcode.png" alt=""/>
|
||||
|
||||
## 🔗 重要链接
|
||||
|
||||
- [🌐 官方文档](https://uview.d3u.cn/)
|
||||
- [📝 更新日志](https://uview.d3u.cn/components/changelog.html)
|
||||
- [👥 关于我们](https://uview.d3u.cn/cooperation/about.html)
|
||||
|
||||
## 🛠️ 关于 PR
|
||||
|
||||
> 我们非常乐意接受各位的优质 PR,但在此之前我希望您了解 uView Next是一个需要兼容多个平台的(小程序、h5、ios app、android app)包括 nvue 页面、vue 页面。
|
||||
> 所以希望在您修复 bug 并提交之前尽可能的去这些平台测试一下兼容性。最好能携带测试截图以方便审核。非常感谢!
|
||||
|
||||
### 📦 快速安装
|
||||
|
||||
请通过[官网安装文档](https://uview.d3u.cn/components/downloadSetting.html)了解更详细的内容
|
||||
|
||||
### 🚀 快速上手
|
||||
|
||||
请通过[快速上手](https://uview.d3u.cn/components/quickstart.html)了解更详细的内容
|
||||
|
||||
## 💡 使用方法
|
||||
|
||||
配置 easycom 规则后,自动按需引入,无需`import`组件,直接引用即可。
|
||||
|
||||
```html
|
||||
<template>
|
||||
<u-button text="按钮"></u-button>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 📄 版权信息
|
||||
|
||||
uView Next遵循[MIT](http://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将 uView 应用到您的产品中。
|
||||
@@ -0,0 +1,675 @@
|
||||
|
||||
## 3.4.8(2025-08-12)
|
||||
1. 新增`subsection`组件`disabled`,`disabledBgColor `,`disabledColor `属性,优化组件
|
||||
## 3.4.7(2025-08-11)
|
||||
1. 修复一些已知问题
|
||||
## 3.4.6(2025-08-11)
|
||||
1. 修复`form`组件校验为空的问题
|
||||
|
||||
## 3.4.5(2025-08-11)
|
||||
1. 新增`sidebar`侧边栏组件
|
||||
|
||||
## 3.4.4(2025-08-10)
|
||||
1. 修复`form`组件`resetFields`方法vue2无效
|
||||
|
||||
## 3.4.3(2025-08-09)
|
||||
1. 修复`barcode`组件异常报错
|
||||
|
||||
## 3.4.2(2025-08-09)
|
||||
1. 新增`actionSheet`组件`height`属性,超出高度自动滚动
|
||||
2. 新增`dateStrip`组件`padZero`,`disabledFun`属性,优化组件
|
||||
|
||||
## 3.4.1(2025-08-08)
|
||||
1. 优化`dateStrip`日期横条组件
|
||||
|
||||
## 3.4.0(2025-08-08)
|
||||
1. 新增`dateStrip`日期横条组件
|
||||
2. 优化`dropdown`组件
|
||||
3. 优化`table`组件
|
||||
|
||||
## 3.3.9(2025-08-07)
|
||||
1. 新增`pdf`组件可预览pdf文件
|
||||
2. 新增`table`组件`headerAlign`,`headerEllipsis`等属性
|
||||
3. 新增`upload`组件`extension`属性
|
||||
|
||||
## 3.3.8(2025-08-05)
|
||||
1. 新增`select`下拉列表组件`keyName`和`valueName`属性
|
||||
|
||||
## 3.3.7(2025-08-05)
|
||||
1. 优化控制台输出
|
||||
|
||||
## 3.3.6(2025-08-04)
|
||||
1. 全面适配鸿蒙系统
|
||||
2. 其他一些细节优化
|
||||
|
||||
## 3.3.5(2025-08-04)
|
||||
1. 新增`barcode`条码组件
|
||||
2. 导出http等方法
|
||||
|
||||
## 3.3.4(2025-08-02)
|
||||
1. 新增`select`下拉列表组件
|
||||
|
||||
## 3.3.3(2025-08-01)
|
||||
1. 优化`signature`组件
|
||||
|
||||
## 3.3.2(2025-07-29)
|
||||
1. 修复`icon`组件使用图片时支付宝显示不全
|
||||
2. 修复`picker`组件支付宝传递this引发的报错
|
||||
3. 修复一些组件由于`canvas`问题引起的支付宝报错
|
||||
4. 修复`calendar`组件, `swipe-action-item`组件抖音小程序报错
|
||||
5. 修复`swipe-action-item`组件不支持支付宝小程序的问题
|
||||
6. 其他一些细节优化
|
||||
|
||||
## 3.3.1(2025-07-28)
|
||||
1. 新增`u-divider` 组件插槽模式
|
||||
2. 新增`form-item`组件`rules`属性
|
||||
3. 修复`form`组件异步校验异常等问题
|
||||
4. 优化`number-box`组件
|
||||
5. 其他一些细节优化
|
||||
|
||||
## 3.3.0(2025-07-25)
|
||||
1. 新增`button`组件新增`iconPosition`属性
|
||||
|
||||
## 3.2.9(2025-07-24)
|
||||
1. 修复`I18n国际化`语言包
|
||||
|
||||
## 3.2.8(2025-07-24)
|
||||
1. 优化`$u.os()`方法,去除小程序下的警告
|
||||
2. 优化`areaPicker`组件input相关属性
|
||||
3. 优化`datetimePicker`组件input相关属性,以及小程序new Date()再ios设备无效的警告
|
||||
4. 优化一些组件在小程序下各种警告
|
||||
5. 修复`overlay`由组件引发的一些异常
|
||||
6. 修复`cropper`组件方法报错,以及缺失showGrid属性
|
||||
7. 其他一些细节优化
|
||||
|
||||
## 3.2.7(2025-07-21)
|
||||
1. 新增`draggable`拖拽排序组件
|
||||
2. 修复`popover`组件边界计算不准确的问题
|
||||
3. 修复几处语法错误导致的小程序无法上传
|
||||
4. 优化`pagination`组件
|
||||
5. 优化`card`组件
|
||||
|
||||
## 3.2.6(2025-07-18)
|
||||
1. 新增`I18n国际化`功能,内置8种语言切换
|
||||
2. 其他一些细节优化
|
||||
|
||||
## 3.2.5(2025-07-17)
|
||||
1. 新增`datetimePicker`组件新增showInput属性
|
||||
2. 新增`areaPicker`组件新增showInput属性
|
||||
3. 修复`calendar`组件`readonly`属性错误
|
||||
|
||||
## 3.2.4(2025-07-16)
|
||||
1. 新增`Ellipsis 文本省略`组件
|
||||
2. 修复`Button`组件主题色问题
|
||||
|
||||
## 3.2.3(2025-07-15)
|
||||
1. 新增`Fab 悬浮按钮`组件
|
||||
2. 新增`upload`组件 `round` 属性,可设置圆角数值
|
||||
3. 其他一些细节优化
|
||||
|
||||
## 3.2.2(2025-07-14)
|
||||
更新文档
|
||||
|
||||
## 3.2.1(2025-07-14)
|
||||
1. 新增`Cascader 级联选择器`组件
|
||||
2. 新增`Tabs`组件使用`u-tab-item`子组件定义方式
|
||||
3. 新增`Radio`组件value支持布尔值
|
||||
## 3.2.0(2025-07-12)
|
||||
1. 修复微信小程序 `{"es6" : false} ` 引发的报错
|
||||
|
||||
## 3.1.9(2025-07-11)
|
||||
1. 优化示例
|
||||
|
||||
## 3.1.8(2025-07-11)
|
||||
1. 新增`pagination`组件
|
||||
|
||||
## 3.1.7(2025-07-10)
|
||||
1. 新增`popover`组件
|
||||
|
||||
## 3.1.6(2025-07-08)
|
||||
1. 修复`scroll-list`组件微信小程序编译后会有警告
|
||||
2. 修复`cell`组件required属性不生效的问题
|
||||
3. 修复`tabs`组件index小于0时自动设置为0
|
||||
4. 修复`keyboard`组件名称冲突的问题
|
||||
5. 修复`datetime-picker`组件日期判断不准确的问题
|
||||
6. 删除`theme.scss`中的flex
|
||||
7. 其他一些修复
|
||||
|
||||
## 3.1.5(2025-07-07)
|
||||
1. 新增`button`组件 `round` 属性,可设置圆角数值
|
||||
2. 新增`modal`组件 `buttonModel` 属性,可以设置按钮样式
|
||||
|
||||
## 3.1.4(2025-07-06)
|
||||
1. 修复vue2和vue3兼容性
|
||||
2. 其他一些修复
|
||||
|
||||
## 3.1.3(2025-07-02)
|
||||
1. 修复 vue2 引入报错
|
||||
2. 新增`tag`组件增加默认 slot
|
||||
3. 其他一些修复
|
||||
|
||||
## 3.1.2(2025-07-01)
|
||||
|
||||
1. 修复`tab`组件文字换行
|
||||
2. 修复`readMore`组件兼容性
|
||||
3. 其他一些修复
|
||||
|
||||
## 3.1.1(2025-07-01)
|
||||
|
||||
1. 新增`calendar`组件新增 shape 属性
|
||||
|
||||
## 3.1.0(2025-06-30)
|
||||
|
||||
1. 新增`calendar`组件新增日期是否被禁用的属性
|
||||
2. 其他一些修复
|
||||
|
||||
## 3.0.9(2025-06-30)
|
||||
|
||||
1. 优化`$u.toast`支持关闭后回调
|
||||
|
||||
## 3.0.8(2025-06-30)
|
||||
|
||||
1. 优化`parse`组件支持解析音频
|
||||
2. 新增`calendar`组件新增插入模式
|
||||
3. 新增`markdown`组件用于 markdown 文档
|
||||
4. 其他一些修复
|
||||
|
||||
## 3.0.7(2025-06-29)
|
||||
|
||||
1. 修改`keyboard`组件键盘自定义样式无效的问题
|
||||
2. 修复`number-box`组件解决输入数字的问题,同时解决小数无法输入的问题
|
||||
3. 修复`claendar`组件选择相同日期重新打开滚动定位错误问题
|
||||
4. 修复`form-item`组件 marginBottom 取值问题
|
||||
5. 修复`textarea`组件字数统计不限制时,会出现/-1
|
||||
6. 修复`input`组件在 h5 平台设置 readonly 时 click 事件失效的问题
|
||||
7. 新增`swiper-action-item`组件的 name 属性可以支持传递对象
|
||||
8. 新增`input`组件增加 always-embed 属性
|
||||
9. 新增`code-input`组件输入框新增 type 属性
|
||||
10. 新增 test.image 函数支持判断 base64 格式的图片
|
||||
|
||||
## 3.0.6(2025-06-28)
|
||||
|
||||
1. 修复`search`在 h5 环境下的点击失效
|
||||
2. 修复`number-box` focus 问题
|
||||
3. 修复倒计时组件样例代码的运行错误
|
||||
4. 修复`index-list`设置 customNavHeight 值问题
|
||||
5. 修复`upload`组件视频无法预览问题
|
||||
6. 修复`datetime-picker` 首次点击 confirm 回调无法获取最新值的问题,新增年,月选择
|
||||
7. 修复`textarea`安卓微信实际输入字数超过限制问题,placeholderClass 和 placeholderStyle 不生效
|
||||
|
||||
## 3.0.5(2025-06-27)
|
||||
|
||||
1. search 组件新增右侧插槽
|
||||
|
||||
## 3.0.4(2025-06-27)
|
||||
|
||||
1. 优化 cell 组件示例
|
||||
|
||||
## 3.0.3(2025-06-26)
|
||||
|
||||
1. 优化懒加载
|
||||
|
||||
## 3.0.0
|
||||
|
||||
1. 兼容 vue3
|
||||
2. 新增组件
|
||||
3. 修改官方 github 大量 bug
|
||||
|
||||
## 2.0.38(2024-10-29)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复`picker`组件和`datetime-picker`组件的头条小程序循环引用问题
|
||||
2. 修复`image`组件的`bgColor`属性不生效问题
|
||||
3. 修复`textarea`组件部分回调事件参数丢失问题
|
||||
4. 修复`input`组件部分回调事件参数丢失问题
|
||||
5. `datetime-picker`组件支持`immediateChange`属性
|
||||
6. 修复`datetime-picker`组件`v-model`绑定异步设置无效问题
|
||||
7. 修复`popup`组件设置`duration`属性为字符串值时,遮罩的打开、关闭很缓慢问题
|
||||
8. 修复`radio`参数`iconColor`属性不生效问题
|
||||
9. 修复`tooltip`组件`size`属性不生效的问题
|
||||
10. 修复`image`组件当`src`为空时,loading 状态问题
|
||||
11. 其他一些修复
|
||||
|
||||
## 2.0.37(2024-03-17)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复表单校验`trigger`触发器参数无效问题
|
||||
2. 修复`u-input`组件的`password`属性在动态切换为`false`时失效的问题
|
||||
3. 添加微信小程序用户同意隐私协议事件回调
|
||||
4. 修复支付宝小程序 picker 样式问题
|
||||
5. `u-modal`添加`duration`字段控制动画过度时间
|
||||
6. 修复`picker` `lastIndex`异常导致的`column`异常问题
|
||||
7. `tabs`增加长按事件支持
|
||||
8. 修复`u-avatar` `square`属性在小程序`open-data`下无效问题
|
||||
9. 其他一些修复
|
||||
|
||||
## 2.0.36(2023-03-27)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 重构`deepClone` & `deepMerge`方法
|
||||
2. 其他优化
|
||||
|
||||
## 2.0.34(2022-09-24)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. `u-input`、`u-textarea`增加`ignoreCompositionEvent`属性
|
||||
2. 修复`route`方法调用可能报错的问题
|
||||
3. 修复`u-no-network`组件`z-index`无效的问题
|
||||
4. 修复`textarea`组件在 h5 上 confirmType=""报错的问题
|
||||
5. `u-rate`适配`nvue`
|
||||
6. 优化验证手机号码的正则表达式(根据工信部发布的《电信网编号计划(2017 年版)》进行修改。)
|
||||
7. `form-item`添加`labelPosition`属性
|
||||
8. `u-calendar`修复`maxDate`设置为当前日期,并且当前时间大于 08:00 时无法显示日期列表的问题 (#724)
|
||||
9. `u-radio`增加一个默认插槽用于自定义修改 label 内容 (#680)
|
||||
10. 修复`timeFormat`函数在 safari 重的兼容性问题 (#664)
|
||||
|
||||
## 2.0.33(2022-06-17)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复`loadmore`组件`lineColor`类型错误问题
|
||||
2. 修复`u-parse`组件`imgtap`、`linktap`不生效问题
|
||||
|
||||
## 2.0.32(2022-06-16)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. `u-loadmore`新增自定义颜色、虚/实线
|
||||
2. 修复`u-swiper-action`组件部分平台不能上下滑动的问题
|
||||
3. 修复`u-list`回弹问题
|
||||
4. 修复`notice-bar`组件动画在低端安卓机可能会抖动的问题
|
||||
5. `u-loading-page`添加控制图标大小的属性`iconSize`
|
||||
6. 修复`u-tooltip`组件`color`参数不生效的问题
|
||||
7. 修复`u--input`组件使用`blur`事件输出为`undefined`的 bug
|
||||
8. `u-code-input`组件新增键盘弹起时,是否自动上推页面参数`adjustPosition`
|
||||
9. 修复`image`组件`load`事件无回调对象问题
|
||||
10. 修复`button`组件`loadingSize`设置无效问题
|
||||
11. 其他修复
|
||||
|
||||
## 2.0.31(2022-04-19)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复`upload`在`vue`页面上传成功后没有成功标志的问题
|
||||
2. 解决演示项目中微信小程序模拟上传图片一直出于上传中问题
|
||||
3. 修复`u-code-input`组件在`nvue`页面编译到`app`平台上光标异常问题(`app`去除此功能)
|
||||
4. 修复`actionSheet`组件标题关闭按钮点击事件名称错误的问题
|
||||
5. 其他修复
|
||||
|
||||
## 2.0.30(2022-04-04)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. `u-rate`增加`readonly`属性
|
||||
2. `tabs`滑块支持设置背景图片
|
||||
3. 修复`u-subsection` `mode`为`subsection`时,滑块样式不正确的问题
|
||||
4. `u-code-input`添加光标效果动画
|
||||
5. 修复`popup`的`open`事件不触发
|
||||
6. 修复`u-flex-column`无效的问题
|
||||
7. 修复`u-datetime-picker`索引在特定场合异常问题
|
||||
8. 修复`u-datetime-picker`最小时间字符串模板错误问题
|
||||
9. `u-swiper`添加`m3u8`验证
|
||||
10. `u-swiper`修改判断 image 和 video 逻辑
|
||||
11. 修复`swiper`无法使用本地图片问题,增加`type`参数
|
||||
12. 修复`u-row-notice`格式错误问题
|
||||
13. 修复`u-switch`组件当`unit`为`rpx`时,`nodeStyle`消失的问题
|
||||
14. 修复`datetime-picker`组件`showToolbar`与`visibleItemCount`属性无效的问题
|
||||
15. 修复`upload`组件条件编译位置判断错误,导致`previewImage`属性设置为`false`时,整个组件都会被隐藏的问题
|
||||
16. 修复`u-checkbox-group`设置`shape`属性无效的问题
|
||||
17. 修复`u-upload`的`capture`传入字符串的时候不生效的问题
|
||||
18. 修复`u-action-sheet`组件,关闭事件逻辑错误的问题
|
||||
19. 修复`u-list`触顶事件的触发错误的问题
|
||||
20. 修复`u-text`只有手机号可拨打的问题
|
||||
21. 修复`u-textarea`不能换行的问题
|
||||
22. 其他修复
|
||||
|
||||
## 2.0.29(2022-03-13)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复`u--text`组件设置`decoration`属性未生效的问题
|
||||
2. 修复`u-datetime-picker`使用`formatter`后返回值不正确
|
||||
3. 修复`u-datetime-picker` `intercept` 可能为 undefined
|
||||
4. 修复已设置单位 uni..config.unit = 'rpx'时,线型指示器 `transform` 的位置翻倍,导致指示器超出宽度
|
||||
5. 修复 mixin 中 bem 方法生成的类名在支付宝和字节小程序中失效
|
||||
6. 修复默认值传值为空的时候,打开`u-datetime-picker`报错,不能选中第一列时间的 bug
|
||||
7. 修复`u-datetime-picker`使用`formatter`后返回值不正确
|
||||
8. 修复`u-image`组件`loading`无效果的问题
|
||||
9. 修复`config.unit`属性设为`rpx`时,导航栏占用高度不足导致塌陷的问题
|
||||
10. 修复`u-datetime-picker`组件`itemHeight`无效问题
|
||||
11. 其他修复
|
||||
|
||||
## 2.0.28(2022-02-22)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. search 组件新增 searchIconSize 属性
|
||||
2. 兼容 Safari/Webkit 中传入时间格式如 2022-02-17 12:00:56
|
||||
3. 修复 text value.js 判断日期出 format 错误问题
|
||||
4. priceFormat 格式化金额出现精度错误
|
||||
5. priceFormat 在部分情况下出现精度损失问题
|
||||
6. 优化表单 rules 提示
|
||||
7. 修复 avatar 组件 src 为空时,展示状态不对
|
||||
8. 其他修复
|
||||
|
||||
## 2.0.27(2022-01-28)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1.样式修复
|
||||
|
||||
## 2.0.26(2022-01-28)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1.样式修复
|
||||
|
||||
## 2.0.25(2022-01-27)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 text 组件 mode=price 时,可能会导致精度错误的问题
|
||||
2. 添加$u.setConfig()方法,可设置 uView 内置的 config, props, zIndex, color 属性,详见:[修改 uView 内置配置方案](https://uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
|
||||
3. 优化 form 组件在 errorType=toast 时,如果输入错误页面会有抖动的问题
|
||||
4. 修复$u.addUnit()对配置默认单位可能无效的问题
|
||||
|
||||
## 2.0.24(2022-01-25)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 swiper 在 current 指定非 0 时缩放有误
|
||||
2. 修复 u-icon 添加 stop 属性的时候报错
|
||||
3. 优化遗留的通过正则判断 rpx 单位的问题
|
||||
4. 优化 Layout 布局 vue 使用 gutter 时,会超出固定区域
|
||||
5. 优化 search 组件高度单位问题(rpx -> px)
|
||||
6. 修复 u-image slot 加载和错误的图片失去了高度
|
||||
7. 修复 u-index-list 中 footer 插槽与 header 插槽存在性判断错误
|
||||
8. 修复部分机型下 u-popup 关闭时会闪烁
|
||||
9. 修复 u-image 在 nvue-app 下失去宽高
|
||||
10. 修复 u-popup 运行报错
|
||||
11. 修复 u-tooltip 报错
|
||||
12. 修复 box-sizing 在 app 下的警告
|
||||
13. 修复 u-navbar 在小程序中报运行时错误
|
||||
14. 其他修复
|
||||
|
||||
## 2.0.23(2022-01-24)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 image 组件在 hx3.3.9 的 nvue 下可能会显示异常的问题
|
||||
2. 修复 col 组件 gutter 参数带 rpx 单位处理不正确的问题
|
||||
3. 修复 text 组件单行时无法显示省略号的问题
|
||||
4. navbar 添加 titleStyle 参数
|
||||
5. 升级到 hx3.3.9 可消除 nvue 下控制台样式警告的问题
|
||||
|
||||
## 2.0.22(2022-01-19)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. $u.page()方法优化,避免在特殊场景可能报错的问题
|
||||
2. picker 组件添加 immediateChange 参数
|
||||
3. 新增$u.pages()方法
|
||||
|
||||
## 2.0.21(2022-01-19)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 优化:form 组件在用户设置 rules 的时候提示用户 model 必传
|
||||
2. 优化遗留的通过正则判断 rpx 单位的问题
|
||||
3. 修复微信小程序环境中 tabbar 组件开启 safeAreaInsetBottom 属性后,placeholder 高度填充不正确
|
||||
4. 修复 swiper 在 current 指定非 0 时缩放有误
|
||||
5. 修复 u-icon 添加 stop 属性的时候报错
|
||||
6. 修复 upload 组件在 accept=all 的时候没有作用
|
||||
7. 修复在 text 组件 mode 为 phone 时 call 属性无效的问题
|
||||
8. 处理 u-form clearValidate 方法
|
||||
9. 其他修复
|
||||
|
||||
## 2.0.20(2022-01-14)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 calendar 默认会选择一个日期,如果直接点确定的话,无法取到值的问题
|
||||
2. 修复 Slider 缺少 disabled props 还有注释
|
||||
3. 修复 u-notice-bar 点击事件无法拿到 index 索引值的问题
|
||||
4. 修复 u-collapse-item 在 vue 文件下,app 端自定义插槽不生效的问题
|
||||
5. 优化头像为空时显示默认头像
|
||||
6. 修复图片地址赋值后判断加载状态为完成问题
|
||||
7. 修复日历滚动到默认日期月份区域
|
||||
8. search 组件暴露点击左边 icon 事件
|
||||
9. 修复 u-form clearValidate 方法不生效
|
||||
10. upload h5 端增加返回文件参数(文件的 name 参数)
|
||||
11. 处理 upload 选择文件后 url 为 blob 类型无法预览的问题
|
||||
12. u-code-input 修复输入框没有往左移出一半屏幕
|
||||
13. 修复 Upload 上传 disabled 为 true 时,控制台报 hoverClass 类型错误
|
||||
14. 临时处理 ios app 下 grid 点击坍塌问题
|
||||
15. 其他修复
|
||||
|
||||
## 2.0.19(2021-12-29)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 优化微信小程序包体积可在微信中预览,请升级 HbuilderX3.3.4,同时在“运行->运行到小程序模拟器”中勾选“运行时是否压缩代码”
|
||||
2. 优化微信小程序 setData 性能,处理某些方法如$u.route()无法在模板中使用的问题
|
||||
3. navbar 添加 autoBack 参数
|
||||
4. 允许 avatar 组件的事件冒泡
|
||||
5. 修复 cell 组件报错问题
|
||||
6. 其他修复
|
||||
|
||||
## 2.0.18(2021-12-28)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 app 端编译报错问题
|
||||
2. 重新处理微信小程序端 setData 过大的性能问题
|
||||
3. 修复边框问题
|
||||
4. 修复最大最小月份不大于 0 则没有数据出现的问题
|
||||
5. 修复 SwipeAction 微信小程序端无法上下滑动问题
|
||||
6. 修复 input 的 placeholder 在小程序端默认显示为 true 问题
|
||||
7. 修复 divider 组件 click 事件无效问题
|
||||
8. 修复 u-code-input maxlength 属性值为 String 类型时显示异常
|
||||
9. 修复当 grid 只有 1 到 2 时 在小程序端 algin 设置无效的问题
|
||||
10. 处理 form-item 的 label 为 top 时,取消错误提示的左边距
|
||||
11. 其他修复
|
||||
|
||||
## 2.0.17(2021-12-26)
|
||||
|
||||
## uView 正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助 uView](https://www.oschina.net/project/top_cn_2021/?id=583)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 解决 HBuilderX3.3.3.20211225 版本导致的样式问题
|
||||
2. calendar 日历添加 monthNum 参数
|
||||
3. navbar 添加 center slot
|
||||
|
||||
## 2.0.16(2021-12-25)
|
||||
|
||||
## uView 正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助 uView](https://www.oschina.net/project/top_cn_2021/?id=583)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 解决微信小程序 setData 性能问题
|
||||
2. 修复 count-down 组件 change 事件不触发问题
|
||||
|
||||
## 2.0.15(2021-12-21)
|
||||
|
||||
## uView 正在参与开源中国的“年度最佳项目”评选,之前投过票的现在也可以投票,恳请同学们投一票,[点此帮助 uView](https://www.oschina.net/project/top_cn_2021/?id=583)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 Cell 单元格 titleWidth 无效
|
||||
2. 修复 cheakbox 组件 ischecked 不更新
|
||||
3. 修复 keyboard 是否显示"."按键默认值问题
|
||||
4. 修复 number-keyboard 是否显示键盘的"."符号问题
|
||||
5. 修复 Input 输入框 readonly 无效
|
||||
6. 修复 u-avatar 导致打包 app、H5 时候报错问题
|
||||
7. 修复 Upload 上传 deletable 无效
|
||||
8. 修复 upload 当设置 maxSize 时无效的问题
|
||||
9. 修复 tabs lineWidth 传入带单位的字符串的时候偏移量计算错误问题
|
||||
10. 修复 rate 组件在有 padding 的 view 内,显示的星星位置和可触摸区域不匹配,无法正常选中星星
|
||||
|
||||
## 2.0.13(2021-12-14)
|
||||
|
||||
## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复配置默认单位为 rpx 可能会导致自定义导航栏高度异常的问题
|
||||
|
||||
## 2.0.12(2021-12-14)
|
||||
|
||||
## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 tabs 组件在 vue 环境下划线消失的问题
|
||||
2. 修复 upload 组件在安卓小程序无法选择视频的问题
|
||||
3. 添加 uni.$u.config.unit 配置,用于配置参数默认单位,详见:[默认单位配置](https://uview.d3u.cn/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
|
||||
4. 修复 textarea 组件在没绑定 v-model 时,字符统计不生效问题
|
||||
5. 修复 nvue 下控制是否出现滚动条失效问题
|
||||
|
||||
## 2.0.11(2021-12-13)
|
||||
|
||||
## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. text 组件 align 参数无效的问题
|
||||
2. subsection 组件添加 keyName 参数
|
||||
3. upload 组件无法判断[Object file]类型的问题
|
||||
4. 处理 notify 层级过低问题
|
||||
5. codeInput 组件添加 disabledDot 参数
|
||||
6. 处理 actionSheet 组件 round 参数无效的问题
|
||||
7. calendar 组件添加 round 参数用于控制圆角值
|
||||
8. 处理 swipeAction 组件在 vue 环境下默认被打开的问题
|
||||
9. button 组件的 throttleTime 节流参数无效的问题
|
||||
10. 解决 u-notify 手动关闭方法 close()无效的问题
|
||||
11. input 组件 readonly 不生效问题
|
||||
12. tag 组件 type 参数为 info 不生效问题
|
||||
|
||||
## 2.0.10(2021-12-08)
|
||||
|
||||
## [点击加群交流反馈:364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 button sendMessagePath 属性不生效
|
||||
2. 修复 DatetimePicker 选择器 title 无效
|
||||
3. 修复 u-toast 设置 loading=true 不生效
|
||||
4. 修复 u-text 金额模式传 0 报错
|
||||
5. 修复 u-toast 组件的 icon 属性配置不生效
|
||||
6. button 的 icon 在特殊场景下的颜色优化
|
||||
7. IndexList 优化,增加#
|
||||
|
||||
## 2.0.9(2021-12-01)
|
||||
|
||||
## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 优化 swiper 的 height 支持 100%值(仅 vue 有效),修复嵌入视频时 click 事件无法触发的问题
|
||||
2. 优化 tabs 组件对 list 值为空的判断,或者动态变化 list 时重新计算相关尺寸的问题
|
||||
3. 优化 datetime-picker 组件逻辑,让其后续打开的默认值为上一次的选中值,需要通过 v-model 绑定值才有效
|
||||
4. 修复 upload 内嵌在其他组件中,选择图片可能不会换行的问题
|
||||
|
||||
## 2.0.8(2021-12-01)
|
||||
|
||||
## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 toast 的 position 参数无效问题
|
||||
2. 处理 input 在 ios nvue 上无法获得焦点的问题
|
||||
3. avatar-group 组件添加 extraValue 参数,让剩余展示数量可手动控制
|
||||
4. tabs 组件添加 keyName 参数用于配置从对象中读取的键名
|
||||
5. 处理 text 组件名字脱敏默认配置无效的问题
|
||||
6. 处理 picker 组件 item 文本太长换行问题
|
||||
|
||||
## 2.0.7(2021-11-30)
|
||||
|
||||
## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 修复 radio 和 checkbox 动态改变 v-model 无效的问题。
|
||||
2. 优化 form 规则 validator 在微信小程序用法
|
||||
3. 修复 backtop 组件 mode 参数在微信小程序无效的问题
|
||||
4. 处理 Album 的 previewFullImage 属性无效的问题
|
||||
5. 处理 u-datetime-picker 组件 mode='time'在选择改变时间时,控制台报错的问题
|
||||
|
||||
## 2.0.6(2021-11-27)
|
||||
|
||||
## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. 处理 tag 组件在 vue 下边框无效的问题。
|
||||
2. 处理 popup 组件圆角参数可能无效的问题。
|
||||
3. 处理 tabs 组件 lineColor 参数可能无效的问题。
|
||||
4. propgress 组件在值很小时,显示异常的问题。
|
||||
|
||||
## 2.0.5(2021-11-25)
|
||||
|
||||
## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. calendar 在 vue 下显示异常问题。
|
||||
2. form 组件 labelPosition 和 errorType 参数无效的问题
|
||||
3. input 组件 inputAlign 无效的问题
|
||||
4. 其他一些修复
|
||||
|
||||
## 2.0.4(2021-11-23)
|
||||
|
||||
## [点击加群交流反馈:232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
0. input 组件缺失@confirm 事件,以及 subfix 和 prefix 无效问题
|
||||
1. component.scss 文件样式在 vue 下干扰全局布局问题
|
||||
2. 修复 subsection 在 vue 环境下表现异常的问题
|
||||
3. tag 组件的 bgColor 等参数无效的问题
|
||||
4. upload 组件不换行的问题
|
||||
5. 其他的一些修复处理
|
||||
|
||||
## 2.0.3(2021-11-16)
|
||||
|
||||
## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. uView2.0 已实现全面兼容 nvue
|
||||
2. uView2.0 对 1.x 进行了架构重构,细节和性能都有极大提升
|
||||
3. 目前 uView2.0 为公测阶段,相关细节可能会有变动
|
||||
4. 我们写了一份与 1.x 的对比指南,详见[对比 1.x](https://uview.d3u.cn/components/diff1.x.html)
|
||||
5. 处理 modal 的 confirm 回调事件拼写错误问题
|
||||
6. 处理 input 组件@input 事件参数错误问题
|
||||
7. 其他一些修复
|
||||
|
||||
## 2.0.2(2021-11-16)
|
||||
|
||||
## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. uView2.0 已实现全面兼容 nvue
|
||||
2. uView2.0 对 1.x 进行了架构重构,细节和性能都有极大提升
|
||||
3. 目前 uView2.0 为公测阶段,相关细节可能会有变动
|
||||
4. 我们写了一份与 1.x 的对比指南,详见[对比 1.x](https://uview.d3u.cn/components/diff1.x.html)
|
||||
5. 修复 input 组件 formatter 参数缺失问题
|
||||
6. 优化 loading-icon 组件的 scss 写法问题,防止不兼容新版本 scss
|
||||
|
||||
## 2.0.0(2020-11-15)
|
||||
|
||||
## [点击加群交流反馈:1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
|
||||
|
||||
# uView2.0 重磅发布,利剑出鞘,一统江湖
|
||||
|
||||
1. uView2.0 已实现全面兼容 nvue
|
||||
2. uView2.0 对 1.x 进行了架构重构,细节和性能都有极大提升
|
||||
3. 目前 uView2.0 为公测阶段,相关细节可能会有变动
|
||||
4. 我们写了一份与 1.x 的对比指南,详见[对比 1.x](https://uview.d3u.cn/components/diff1.x.html)
|
||||
5. 修复 input 组件 formatter 参数缺失问题
|
||||
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<uvForm
|
||||
ref="uForm"
|
||||
:model="model"
|
||||
@update:model="$emit('update:model', $event)"
|
||||
:rules="rules"
|
||||
:errorType="errorType"
|
||||
:borderBottom="borderBottom"
|
||||
:labelPosition="labelPosition"
|
||||
:labelWidth="labelWidth"
|
||||
:labelAlign="labelAlign"
|
||||
:labelStyle="labelStyle"
|
||||
:customStyle="customStyle"
|
||||
>
|
||||
<slot />
|
||||
</uvForm>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件
|
||||
* 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转
|
||||
*/
|
||||
import uvForm from '../u-form/u-form.vue';
|
||||
import props from '../u-form/props.js'
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin'
|
||||
/**
|
||||
* Form 表单
|
||||
* @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
|
||||
* @tutorial https://uview.d3u.cn/components/form.html
|
||||
* @property {Object} model 当前form的需要验证字段的集合
|
||||
* @property {Object | Function | Array} rules 验证规则
|
||||
* @property {String} errorType 错误的提示方式,见上方说明 ( 默认 message )
|
||||
* @property {Boolean} borderBottom 是否显示表单域的下划线边框 ( 默认 true )
|
||||
* @property {String} labelPosition 表单域提示文字的位置,left-左侧,top-上方 ( 默认 'left' )
|
||||
* @property {String | Number} labelWidth 提示文字的宽度,单位px ( 默认 45 )
|
||||
* @property {String} labelAlign lable字体的对齐方式 ( 默认 ‘left' )
|
||||
* @property {Object} labelStyle lable的样式,对象形式
|
||||
* @example <u--formlabelPosition="left" :model="model1" :rules="rules" ref="form1"></u--form>
|
||||
*/
|
||||
export default {
|
||||
// #ifdef MP-WEIXIN
|
||||
name: 'u-form',
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
name: 'u--form',
|
||||
// #endif
|
||||
mixins: [mpMixin, mixin, props],
|
||||
components: {
|
||||
uvForm
|
||||
},
|
||||
created() {
|
||||
this.children = []
|
||||
},
|
||||
methods: {
|
||||
// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
|
||||
setRules(rules) {
|
||||
this.$refs.uForm.setRules(rules)
|
||||
},
|
||||
validate() {
|
||||
/**
|
||||
* 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form
|
||||
* 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的
|
||||
* 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children
|
||||
*/
|
||||
// #ifdef MP-WEIXIN
|
||||
this.setMpData()
|
||||
// #endif
|
||||
return this.$refs.uForm.validate()
|
||||
},
|
||||
validateField(value, callback, event) {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.setMpData()
|
||||
// #endif
|
||||
return this.$refs.uForm.validateField(value, callback, event)
|
||||
},
|
||||
resetFields() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.setMpData()
|
||||
// #endif
|
||||
return this.$refs.uForm.resetFields()
|
||||
},
|
||||
clearValidate(props) {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.setMpData()
|
||||
// #endif
|
||||
return this.$refs.uForm.clearValidate(props)
|
||||
},
|
||||
setMpData() {
|
||||
this.$refs.uForm.children = this.children
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<uvImage
|
||||
:src="src"
|
||||
:mode="mode"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:shape="shape"
|
||||
:round="round"
|
||||
:lazyLoad="lazyLoad"
|
||||
:showMenuByLongpress="showMenuByLongpress"
|
||||
:loadingIcon="loadingIcon"
|
||||
:errorIcon="errorIcon"
|
||||
:showLoading="showLoading"
|
||||
:showError="showError"
|
||||
:fade="fade"
|
||||
:webp="webp"
|
||||
:duration="duration"
|
||||
:bgColor="bgColor"
|
||||
:customStyle="customStyle"
|
||||
@click="$emit('click')"
|
||||
@error="$emit('error')"
|
||||
@load="$emit('load')"
|
||||
>
|
||||
<template v-slot:loading>
|
||||
<slot name="loading"></slot>
|
||||
</template>
|
||||
<template v-slot:error>
|
||||
<slot name="error"></slot>
|
||||
</template>
|
||||
</uvImage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件
|
||||
* 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转
|
||||
*/
|
||||
import uvImage from '../u-image/u-image.vue';
|
||||
import props from '../u-image/props.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin'
|
||||
/**
|
||||
* Image 图片
|
||||
* @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
|
||||
* @tutorial https://uviewui.com/components/image.html
|
||||
* @property {String} src 图片地址
|
||||
* @property {String} mode 裁剪模式,见官网说明 (默认 'aspectFill' )
|
||||
* @property {String | Number} width 宽度,单位任意,如果为数值,则为px单位 (默认 '300' )
|
||||
* @property {String | Number} height 高度,单位任意,如果为数值,则为px单位 (默认 '225' )
|
||||
* @property {String} shape 图片形状,circle-圆形,square-方形 (默认 'square' )
|
||||
* @property {String | Number} round 圆角值,单位任意,如果为数值,则为px单位 (默认 0 )
|
||||
* @property {Boolean} lazyLoad 是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效 (默认 true )
|
||||
* @property {Boolean} showMenuByLongpress 是否开启长按图片显示识别小程序码菜单,仅微信小程序有效 (默认 true )
|
||||
* @property {String} loadingIcon 加载中的图标,或者小图片 (默认 'photo' )
|
||||
* @property {String} errorIcon 加载失败的图标,或者小图片 (默认 'error-circle' )
|
||||
* @property {Boolean} showLoading 是否显示加载中的图标或者自定义的slot (默认 true )
|
||||
* @property {Boolean} showError 是否显示加载错误的图标或者自定义的slot (默认 true )
|
||||
* @property {Boolean} fade 是否需要淡入效果 (默认 true )
|
||||
* @property {Boolean} webp 只支持网络资源,只对微信小程序有效 (默认 false )
|
||||
* @property {String | Number} duration 搭配fade参数的过渡时间,单位ms (默认 500 )
|
||||
* @property {String} bgColor 背景颜色,用于深色页面加载图片时,为了和背景色融合 (默认 '#f3f4f6' )
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
* @event {Function} click 点击图片时触发
|
||||
* @event {Function} error 图片加载失败时触发
|
||||
* @event {Function} load 图片加载成功时触发
|
||||
* @example <u--image width="100%" height="300px" :src="src"></u--image>
|
||||
*/
|
||||
export default {
|
||||
name: 'u--image',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
components: {
|
||||
uvImage
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<uvInput
|
||||
<!-- #ifdef VUE2 -->
|
||||
:value="value"
|
||||
@input="e => $emit('input', e)"
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef VUE3 -->
|
||||
:modelValue="modelValue"
|
||||
@update:modelValue="e => $emit('update:modelValue', e)"
|
||||
<!-- #endif -->
|
||||
:type="type"
|
||||
:fixed="fixed"
|
||||
:disabled="disabled"
|
||||
:disabledColor="disabledColor"
|
||||
:clearable="clearable"
|
||||
:password="password"
|
||||
:maxlength="maxlength"
|
||||
:placeholder="placeholder"
|
||||
:placeholderClass="placeholderClass"
|
||||
:placeholderStyle="placeholderStyle"
|
||||
:confirmType="confirmType"
|
||||
:confirmHold="confirmHold"
|
||||
:holdKeyboard="holdKeyboard"
|
||||
:focus="focus"
|
||||
:autoBlur="autoBlur"
|
||||
:disableDefaultPadding="disableDefaultPadding"
|
||||
:alwaysEmbed="alwaysEmbed"
|
||||
:cursor="cursor"
|
||||
:cursorSpacing="cursorSpacing"
|
||||
:selectionStart="selectionStart"
|
||||
:selectionEnd="selectionEnd"
|
||||
:adjustPosition="adjustPosition"
|
||||
:inputAlign="inputAlign"
|
||||
:fontSize="fontSize"
|
||||
:color="color"
|
||||
:backgroundColor="backgroundColor"
|
||||
:round="round"
|
||||
:borderColor="borderColor"
|
||||
:prefixIcon="prefixIcon"
|
||||
:suffixIcon="suffixIcon"
|
||||
:suffixIconStyle="suffixIconStyle"
|
||||
:prefixIconStyle="prefixIconStyle"
|
||||
:border="border"
|
||||
:readonly="readonly"
|
||||
:shape="shape"
|
||||
:customStyle="customStyle"
|
||||
:formatter="formatter"
|
||||
:ignoreCompositionEvent="ignoreCompositionEvent"
|
||||
@focus="e => $emit('focus', e)"
|
||||
@blur="e => $emit('blur', e)"
|
||||
@keyboardheightchange="e => $emit('keyboardheightchange', e)"
|
||||
@change="e => $emit('change', e)"
|
||||
@confirm="e => $emit('confirm', e)"
|
||||
@clear="$emit('clear')"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<!-- #ifdef MP -->
|
||||
<slot name="prefix"></slot>
|
||||
<slot name="suffix"></slot>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP -->
|
||||
<slot name="prefix" slot="prefix"></slot>
|
||||
<slot name="suffix" slot="suffix"></slot>
|
||||
<!-- #endif -->
|
||||
</uvInput>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件
|
||||
* 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转
|
||||
*/
|
||||
import uvInput from '../u-input/u-input.vue';
|
||||
import props from '../u-input/props.js'
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin'
|
||||
/**
|
||||
* Input 输入框
|
||||
* @description 此组件为一个输入框,默认没有边框和样式,是专门为配合表单组件u-form而设计的,利用它可以快速实现表单验证,输入内容,下拉选择等功能。
|
||||
* @tutorial https://uviewui.com/components/input.html
|
||||
* @property {String | Number} value 输入的值
|
||||
* @property {String} type 输入框类型,见上方说明 ( 默认 'text' )
|
||||
* @property {Boolean} fixed 如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true,兼容性:微信小程序、百度小程序、字节跳动小程序、QQ小程序 ( 默认 false )
|
||||
* @property {Boolean} disabled 是否禁用输入框 ( 默认 false )
|
||||
* @property {String} disabledColor 禁用状态时的背景色( 默认 '#f5f7fa' )
|
||||
* @property {Boolean} clearable 是否显示清除控件 ( 默认 false )
|
||||
* @property {Boolean} password 是否密码类型 ( 默认 false )
|
||||
* @property {String | Number} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度 ( 默认 -1 )
|
||||
* @property {String} placeholder 输入框为空时的占位符
|
||||
* @property {String} placeholderClass 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' )
|
||||
* @property {String | Object} placeholderStyle 指定placeholder的样式,字符串/对象形式,如"color: red;"
|
||||
* @property {String} confirmType 设置右下角按钮的文字,兼容性详见uni-app文档 ( 默认 'done' )
|
||||
* @property {Boolean} confirmHold 点击键盘右下角按钮时是否保持键盘不收起,H5无效 ( 默认 false )
|
||||
* @property {Boolean} holdKeyboard focus时,点击页面的时候不收起键盘,微信小程序有效 ( 默认 false )
|
||||
* @property {Boolean} focus 自动获取焦点,在 H5 平台能否聚焦以及软键盘是否跟随弹出,取决于当前浏览器本身的实现。nvue 页面不支持,需使用组件的 focus()、blur() 方法控制焦点 ( 默认 false )
|
||||
* @property {Boolean} autoBlur 键盘收起时,是否自动失去焦点,目前仅App3.0.0+有效 ( 默认 false )
|
||||
* @property {Boolean} disableDefaultPadding 是否去掉 iOS 下的默认内边距,仅微信小程序,且type=textarea时有效 ( 默认 false )
|
||||
* @property {String | Number} cursor 指定focus时光标的位置( 默认 -1 )
|
||||
* @property {String | Number} cursorSpacing 输入框聚焦时底部与键盘的距离 ( 默认 30 )
|
||||
* @property {String | Number} selectionStart 光标起始位置,自动聚集时有效,需与selection-end搭配使用 ( 默认 -1 )
|
||||
* @property {String | Number} selectionEnd 光标结束位置,自动聚集时有效,需与selection-start搭配使用 ( 默认 -1 )
|
||||
* @property {Boolean} adjustPosition 键盘弹起时,是否自动上推页面 ( 默认 true )
|
||||
* @property {String} inputAlign 输入框内容对齐方式( 默认 'left' )
|
||||
* @property {String | Number} fontSize 输入框字体的大小 ( 默认 '15px' )
|
||||
* @property {String} color 输入框字体颜色 ( 默认 '#303133' )
|
||||
* @property {Function} formatter 内容式化函数
|
||||
* @property {String} prefixIcon 输入框前置图标
|
||||
* @property {String | Object} prefixIconStyle 前置图标样式,对象或字符串
|
||||
* @property {String} suffixIcon 输入框后置图标
|
||||
* @property {String | Object} suffixIconStyle 后置图标样式,对象或字符串
|
||||
* @property {String} border 边框类型,surround-四周边框,bottom-底部边框,none-无边框 ( 默认 'surround' )
|
||||
* @property {Boolean} readonly 是否只读,与disabled不同之处在于disabled会置灰组件,而readonly则不会 ( 默认 false )
|
||||
* @property {String} shape 输入框形状,circle-圆形,square-方形 ( 默认 'square' )
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
* @property {Boolean} ignoreCompositionEvent 是否忽略组件内对文本合成系统事件的处理。
|
||||
* @example <u--input v-model="value" :password="true" suffix-icon="lock-fill" />
|
||||
*/
|
||||
export default {
|
||||
name: 'u--input',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
components: {
|
||||
uvInput
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<uvText
|
||||
:type="type"
|
||||
:show="show"
|
||||
:text="text"
|
||||
:prefixIcon="prefixIcon"
|
||||
:suffixIcon="suffixIcon"
|
||||
:mode="mode"
|
||||
:href="href"
|
||||
:format="format"
|
||||
:call="call"
|
||||
:openType="openType"
|
||||
:bold="bold"
|
||||
:block="block"
|
||||
:lines="lines"
|
||||
:color="color"
|
||||
:decoration="decoration"
|
||||
:size="size"
|
||||
:iconStyle="iconStyle"
|
||||
:margin="margin"
|
||||
:lineHeight="lineHeight"
|
||||
:align="align"
|
||||
:wordWrap="wordWrap"
|
||||
:customStyle="customStyle"
|
||||
@click="$emit('click')"
|
||||
></uvText>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件
|
||||
* 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转
|
||||
* 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法
|
||||
*/
|
||||
import uvText from "../u-text/u-text.vue";
|
||||
import props from "../u-text/props.js";
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin'
|
||||
/**
|
||||
* Text 文本
|
||||
* @description 此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件几乎涵盖您能使用的大部分场景。
|
||||
* @tutorial https://uview.d3u.cn/components/loading.html
|
||||
* @property {String} type 主题颜色
|
||||
* @property {Boolean} show 是否显示(默认 true )
|
||||
* @property {String | Number} text 显示的值
|
||||
* @property {String} prefixIcon 前置图标
|
||||
* @property {String} suffixIcon 后置图标
|
||||
* @property {String} mode 文本处理的匹配模式 text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接
|
||||
* @property {String} href mode=link下,配置的链接
|
||||
* @property {String | Function} format 格式化规则
|
||||
* @property {Boolean} call mode=phone时,点击文本是否拨打电话(默认 false )
|
||||
* @property {String} openType 小程序的打开方式
|
||||
* @property {Boolean} bold 是否粗体,默认normal(默认 false )
|
||||
* @property {Boolean} block 是否块状(默认 false )
|
||||
* @property {String | Number} lines 文本显示的行数,如果设置,超出此行数,将会显示省略号
|
||||
* @property {String} color 文本颜色(默认 '#303133' )
|
||||
* @property {String | Number} size 字体大小(默认 15 )
|
||||
* @property {Object | String} iconStyle 图标的样式 (默认 {fontSize: '15px'} )
|
||||
* @property {String} decoration 文字装饰,下划线,中划线等,可选值 none|underline|line-through(默认 'none' )
|
||||
* @property {Object | String | Number} margin 外边距,对象、字符串,数值形式均可(默认 0 )
|
||||
* @property {String | Number} lineHeight 文本行高
|
||||
* @property {String} align 文本对齐方式,可选值left|center|right(默认 'left' )
|
||||
* @property {String} wordWrap 文字换行,可选值break-word|normal|anywhere(默认 'normal' )
|
||||
* @event {Function} click 点击触发事件
|
||||
* @example <u--text text="我用十年青春,赴你最后之约"></u--text>
|
||||
*/
|
||||
export default {
|
||||
name: "u--text",
|
||||
mixins: [mpMixin, mixin, props],
|
||||
components: {
|
||||
uvText,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<uvTextarea
|
||||
<!-- #ifdef VUE2 -->
|
||||
:value="value"
|
||||
@input="e => $emit('input', e)"
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef VUE3 -->
|
||||
:modelValue="modelValue"
|
||||
@update:modelValue="e => $emit('update:modelValue', e)"
|
||||
<!-- #endif -->
|
||||
:placeholder="placeholder"
|
||||
:placeholderStyle="placeholderStyle"
|
||||
:placeholderClass="placeholderClass"
|
||||
:height="height"
|
||||
:confirmType="confirmType"
|
||||
:disabled="disabled"
|
||||
:count="count"
|
||||
:focus="focus"
|
||||
:autoHeight="autoHeight"
|
||||
:fixed="fixed"
|
||||
:cursorSpacing="cursorSpacing"
|
||||
:cursor="cursor"
|
||||
:showConfirmBar="showConfirmBar"
|
||||
:selectionStart="selectionStart"
|
||||
:selectionEnd="selectionEnd"
|
||||
:adjustPosition="adjustPosition"
|
||||
:disableDefaultPadding="disableDefaultPadding"
|
||||
:holdKeyboard="holdKeyboard"
|
||||
:maxlength="maxlength"
|
||||
:border="border"
|
||||
:round="round"
|
||||
:borderColor="borderColor"
|
||||
:backgroundColor="backgroundColor"
|
||||
:customStyle="customStyle"
|
||||
:formatter="formatter"
|
||||
:ignoreCompositionEvent="ignoreCompositionEvent"
|
||||
@focus="e => $emit('focus', e)"
|
||||
@blur="e => $emit('blur', e)"
|
||||
@linechange="e => $emit('linechange', e)"
|
||||
@confirm="e => $emit('confirm', e)"
|
||||
@keyboardheightchange="e => $emit('keyboardheightchange', e)"
|
||||
></uvTextarea>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件
|
||||
* 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转
|
||||
*/
|
||||
import uvTextarea from '../u-textarea/u-textarea.vue';
|
||||
import props from '../u-textarea/props.js'
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin'
|
||||
/**
|
||||
* Textarea 文本域
|
||||
* @description 文本域此组件满足了可能出现的表单信息补充,编辑等实际逻辑的功能,内置了字数校验等
|
||||
* @tutorial https://uview.d3u.cn/components/textarea.html
|
||||
*
|
||||
* @property {String | Number} value 输入框的内容
|
||||
* @property {String | Number} placeholder 输入框为空时占位符
|
||||
* @property {String} placeholderClass 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' )
|
||||
* @property {String | Object} placeholderStyle 指定placeholder的样式,字符串/对象形式,如"color: red;"
|
||||
* @property {String | Number} height 输入框高度(默认 70 )
|
||||
* @property {String} confirmType 设置键盘右下角按钮的文字,仅微信小程序,App-vue和H5有效(默认 'done' )
|
||||
* @property {Boolean} disabled 是否禁用(默认 false )
|
||||
* @property {Boolean} count 是否显示统计字数(默认 false )
|
||||
* @property {Boolean} focus 是否自动获取焦点,nvue不支持,H5取决于浏览器的实现(默认 false )
|
||||
* @property {Boolean | Function} autoHeight 是否自动增加高度(默认 false )
|
||||
* @property {Boolean} fixed 如果textarea是在一个position:fixed的区域,需要显示指定属性fixed为true(默认 false )
|
||||
* @property {Number} cursorSpacing 指定光标与键盘的距离(默认 0 )
|
||||
* @property {String | Number} cursor 指定focus时的光标位置
|
||||
* @property {Function} formatter 内容式化函数
|
||||
* @property {Boolean} showConfirmBar 是否显示键盘上方带有”完成“按钮那一栏,(默认 true )
|
||||
* @property {Number} selectionStart 光标起始位置,自动聚焦时有效,需与selection-end搭配使用,(默认 -1 )
|
||||
* @property {Number | Number} selectionEnd 光标结束位置,自动聚焦时有效,需与selection-start搭配使用(默认 -1 )
|
||||
* @property {Boolean} adjustPosition 键盘弹起时,是否自动上推页面(默认 true )
|
||||
* @property {Boolean | Number} disableDefaultPadding 是否去掉 iOS 下的默认内边距,只微信小程序有效(默认 false )
|
||||
* @property {Boolean} holdKeyboard focus时,点击页面的时候不收起键盘,只微信小程序有效(默认 false )
|
||||
* @property {String | Number} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认 140 )
|
||||
* @property {String} border 边框类型,surround-四周边框,none-无边框,bottom-底部边框(默认 'surround' )
|
||||
* @property {Boolean} ignoreCompositionEvent 是否忽略组件内对文本合成系统事件的处理
|
||||
* @property {String} backgroundColor 背景颜色
|
||||
*@property {String} round 边框圆角
|
||||
* @property {String} borderColor 边框颜色
|
||||
*
|
||||
*
|
||||
* @event {Function(e)} focus 输入框聚焦时触发,event.detail = { value, height },height 为键盘高度
|
||||
* @event {Function(e)} blur 输入框失去焦点时触发,event.detail = {value, cursor}
|
||||
* @event {Function(e)} linechange 输入框行数变化时调用,event.detail = {height: 0, heightRpx: 0, lineCount: 0}
|
||||
* @event {Function(e)} input 当键盘输入时,触发 input 事件
|
||||
* @event {Function(e)} confirm 点击完成时, 触发 confirm 事件
|
||||
* @event {Function(e)} keyboardheightchange 键盘高度发生变化的时候触发此事件
|
||||
* @example <u--textarea v-model="value1" placeholder="请输入内容" ></u--textarea>
|
||||
*/
|
||||
export default {
|
||||
name: 'u--textarea',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
components: {
|
||||
uvTextarea
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
export default {
|
||||
props: {
|
||||
// 操作菜单是否展示 (默认false)
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.actionSheet.show
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.actionSheet.title
|
||||
},
|
||||
// 选项上方的描述信息
|
||||
description: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.actionSheet.description
|
||||
},
|
||||
// 数据
|
||||
actions: {
|
||||
type: Array,
|
||||
default: ()=> uni.$u.props.actionSheet.actions
|
||||
},
|
||||
// 取消按钮的文字,不为空时显示按钮
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.actionSheet.cancelText
|
||||
},
|
||||
// 点击某个菜单项时是否关闭弹窗
|
||||
closeOnClickAction: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.actionSheet.closeOnClickAction
|
||||
},
|
||||
// 处理底部安全区(默认true)
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.actionSheet.safeAreaInsetBottom
|
||||
},
|
||||
// 小程序的打开方式
|
||||
openType: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.actionSheet.openType
|
||||
},
|
||||
// 点击遮罩是否允许关闭 (默认true)
|
||||
closeOnClickOverlay: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.actionSheet.closeOnClickOverlay
|
||||
},
|
||||
// 圆角值
|
||||
round: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.actionSheet.round
|
||||
},
|
||||
// 高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.actionSheet.height
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<u-popup :show="show" mode="bottom" @close="closeHandler" :safeAreaInsetBottom="safeAreaInsetBottom" :round="round">
|
||||
<view class="u-action-sheet">
|
||||
<view class="u-action-sheet__header" v-if="title">
|
||||
<text class="u-action-sheet__header__title u-line-1">{{ title }}</text>
|
||||
<view class="u-action-sheet__header__icon-wrap" @tap.stop="cancel">
|
||||
<u-icon name="close" size="17" color="#c8c9cc" bold></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<text class="u-action-sheet__description" :style="[{
|
||||
marginTop: `${title && description ? 0 : '18px'}`
|
||||
}]" v-if="description">{{ description }}</text>
|
||||
<slot>
|
||||
<u-line v-if="description"></u-line>
|
||||
<scroll-view class="u-action-sheet__item-wrap" :scroll-y="height !='' || height > 0" :style="[{ maxHeight: $u.addUnit(height) }]">
|
||||
<view v-for="(item, index) in actions" :key="index">
|
||||
<!-- #ifdef MP -->
|
||||
<button
|
||||
class="u-reset-button"
|
||||
:key="index"
|
||||
:openType="item.openType"
|
||||
:lang="lang"
|
||||
:session-from="sessionFrom"
|
||||
:send-message-title="sendMessageTitle"
|
||||
:send-message-path="sendMessagePath"
|
||||
:send-message-img="sendMessageImg"
|
||||
:show-message-card="showMessageCard"
|
||||
:app-parameter="appParameter"
|
||||
@tap="selectHandler(index)"
|
||||
@getuserinfo="onGetUserInfo"
|
||||
@contact="onContact"
|
||||
@getphonenumber="onGetPhoneNumber"
|
||||
@error="onError"
|
||||
@launchapp="onLaunchApp"
|
||||
@opensetting="onOpenSetting"
|
||||
@chooseavatar="onChooseAvatar"
|
||||
:hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
|
||||
>
|
||||
<!-- #endif -->
|
||||
<view class="u-action-sheet__item-wrap__item" @tap.stop="selectHandler(index)"
|
||||
:hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
|
||||
:hover-stay-time="150">
|
||||
<template v-if="!item.loading">
|
||||
<text class="u-action-sheet__item-wrap__item__name" :style="[itemStyle(index)]">{{
|
||||
item.name }}</text>
|
||||
<text v-if="item.subname" class="u-action-sheet__item-wrap__item__subname">{{
|
||||
item.subname }}</text>
|
||||
</template>
|
||||
<u-loading-icon v-else custom-class="van-action-sheet__loading" size="18" mode="circle" />
|
||||
</view>
|
||||
<!-- #ifdef MP -->
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
<u-line v-if="index !== actions.length - 1"></u-line>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</slot>
|
||||
<u-gap bgColor="#eaeaec" height="6" v-if="cancelText"></u-gap>
|
||||
<view hover-class="u-action-sheet--hover" v-if="cancelText" @tap="cancel">
|
||||
<text
|
||||
@touchmove.stop.prevent
|
||||
:hover-stay-time="150"
|
||||
class="u-action-sheet__cancel-text"
|
||||
>{{ cancelText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import openType from '../../libs/mixin/openType';
|
||||
import button from '../../libs/mixin/button';
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
/**
|
||||
* ActionSheet 操作菜单
|
||||
* @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
|
||||
* @tutorial https://uview.d3u.cn/components/actionSheet.html
|
||||
*
|
||||
* @property {Boolean} show 操作菜单是否展示 (默认 false )
|
||||
* @property {String} title 操作菜单标题
|
||||
* @property {String} description 选项上方的描述信息
|
||||
* @property {Array<Object>} actions 按钮的文字数组,见官方文档示例
|
||||
* @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮
|
||||
* @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 (默认 true )
|
||||
* @property {Boolean} safeAreaInsetBottom 处理底部安全区 (默认 true )
|
||||
* @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting |getPhoneNumber |error )
|
||||
* @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true )
|
||||
* @property {Number|String} round 圆角值,默认无圆角 (默认 0 )
|
||||
* @property {Number|String} height 高度,默认不限制 (默认 '' )
|
||||
* @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文
|
||||
* @property {String} sessionFrom 会话来源,openType="contact"时有效
|
||||
* @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效
|
||||
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效
|
||||
* @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效
|
||||
* @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效 (默认 false )
|
||||
* @property {String} appParameter 打开 APP 时,向 APP 传递的参数,openType=launchApp 时有效
|
||||
*
|
||||
* @event {Function} select 点击ActionSheet列表项时触发
|
||||
* @event {Function} close 点击取消按钮时触发
|
||||
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
|
||||
* @event {Function} contact 客服消息回调,openType="contact"时有效
|
||||
* @event {Function} getphonenumber 获取用户手机号回调,openType="getPhoneNumber"时有效
|
||||
* @event {Function} chooseavatar 选择头像回调,openType="chooseAvatar"时有效
|
||||
* @event {Function} error 当使用开放能力时,发生错误的回调,openType="error"时有效
|
||||
* @event {Function} launchapp 打开 APP 成功的回调,openType="launchApp"时有效
|
||||
* @event {Function} opensetting 在打开授权设置页后回调,openType="openSetting"时有效
|
||||
* @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet>
|
||||
*/
|
||||
export default {
|
||||
name: "u-action-sheet",
|
||||
// 一些props参数和methods方法,通过mixin混入,因为其他文件也会用到
|
||||
mixins: [openType, button, mixin,mpMixin, props],
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 操作项目的样式
|
||||
itemStyle() {
|
||||
return (index) => {
|
||||
let style = {};
|
||||
if (this.actions[index].color) style.color = this.actions[index].color;
|
||||
if (this.actions[index].fontSize) style.fontSize = uni.$u.addUnit(this.actions[index].fontSize);
|
||||
// 选项被禁用的样式
|
||||
if (this.actions[index].disabled) style.color = '#c0c4cc';
|
||||
return style;
|
||||
};
|
||||
},
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ["select", "close"],
|
||||
// #endif
|
||||
methods: {
|
||||
closeHandler() {
|
||||
// 允许点击遮罩关闭时,才发出close事件
|
||||
if (this.closeOnClickOverlay) {
|
||||
this.$emit('close');
|
||||
}
|
||||
},
|
||||
// 点击取消按钮
|
||||
cancel() {
|
||||
this.$emit('close');
|
||||
},
|
||||
selectHandler(index) {
|
||||
const item = this.actions[index];
|
||||
if (item && !item.disabled && !item.loading) {
|
||||
this.$emit('select', item);
|
||||
if (this.closeOnClickAction) {
|
||||
this.$emit('close');
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
$u-action-sheet-reset-button-width: 100% !default;
|
||||
$u-action-sheet-title-font-size: 16px !default;
|
||||
$u-action-sheet-title-padding: 12px 30px !default;
|
||||
$u-action-sheet-title-color: $u-main-color !default;
|
||||
$u-action-sheet-header-icon-wrap-right: 15px !default;
|
||||
$u-action-sheet-header-icon-wrap-top: 15px !default;
|
||||
$u-action-sheet-description-font-size: 13px !default;
|
||||
$u-action-sheet-description-color: 14px !default;
|
||||
$u-action-sheet-description-margin: 18px 15px !default;
|
||||
$u-action-sheet-item-wrap-item-padding: 15px !default;
|
||||
$u-action-sheet-item-wrap-name-font-size: 16px !default;
|
||||
$u-action-sheet-item-wrap-subname-font-size: 13px !default;
|
||||
$u-action-sheet-item-wrap-subname-color: #c0c4cc !default;
|
||||
$u-action-sheet-item-wrap-subname-margin-top: 10px !default;
|
||||
$u-action-sheet-cancel-text-font-size: 16px !default;
|
||||
$u-action-sheet-cancel-text-color: $u-content-color !default;
|
||||
$u-action-sheet-cancel-text-font-size: 15px !default;
|
||||
$u-action-sheet-cancel-text-hover-background-color: rgb(242, 243, 245) !default;
|
||||
|
||||
.u-reset-button {
|
||||
width: $u-action-sheet-reset-button-width;
|
||||
}
|
||||
|
||||
.u-action-sheet {
|
||||
text-align: center;
|
||||
|
||||
&__header {
|
||||
position: relative;
|
||||
padding: $u-action-sheet-title-padding;
|
||||
|
||||
&__title {
|
||||
font-size: $u-action-sheet-title-font-size;
|
||||
color: $u-action-sheet-title-color;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__icon-wrap {
|
||||
position: absolute;
|
||||
right: $u-action-sheet-header-icon-wrap-right;
|
||||
top: $u-action-sheet-header-icon-wrap-top;
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
font-size: $u-action-sheet-description-font-size;
|
||||
color: $u-tips-color;
|
||||
margin: $u-action-sheet-description-margin;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__item-wrap {
|
||||
|
||||
&__item {
|
||||
padding: $u-action-sheet-item-wrap-item-padding;
|
||||
@include flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
&__name {
|
||||
font-size: $u-action-sheet-item-wrap-name-font-size;
|
||||
color: $u-main-color;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__subname {
|
||||
font-size: $u-action-sheet-item-wrap-subname-font-size;
|
||||
color: $u-action-sheet-item-wrap-subname-color;
|
||||
margin-top: $u-action-sheet-item-wrap-subname-margin-top;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__cancel-text {
|
||||
font-size: $u-action-sheet-cancel-text-font-size;
|
||||
color: $u-action-sheet-cancel-text-color;
|
||||
text-align: center;
|
||||
padding: $u-action-sheet-cancel-text-font-size;
|
||||
}
|
||||
|
||||
&--hover {
|
||||
background-color: $u-action-sheet-cancel-text-hover-background-color;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
export default {
|
||||
props: {
|
||||
// 图片地址,Array<String>|Array<Object>形式
|
||||
urls: {
|
||||
type: Array,
|
||||
default: ()=> uni.$u.props.album.urls
|
||||
},
|
||||
// 指定从数组的对象元素中读取哪个属性作为图片地址
|
||||
keyName: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.album.keyName
|
||||
},
|
||||
// 单图时,图片长边的长度
|
||||
singleSize: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.album.singleSize
|
||||
},
|
||||
// 多图时,图片边长
|
||||
multipleSize: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.album.multipleSize
|
||||
},
|
||||
// 多图时,图片水平和垂直之间的间隔
|
||||
space: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.album.space
|
||||
},
|
||||
// 单图时,图片缩放裁剪的模式
|
||||
singleMode: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.album.singleMode
|
||||
},
|
||||
// 多图时,图片缩放裁剪的模式
|
||||
multipleMode: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.album.multipleMode
|
||||
},
|
||||
// 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
|
||||
maxCount: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.album.maxCount
|
||||
},
|
||||
// 是否可以预览图片
|
||||
previewFullImage: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.album.previewFullImage
|
||||
},
|
||||
// 每行展示图片数量,如设置,singleSize和multipleSize将会无效
|
||||
rowCount: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.album.rowCount
|
||||
},
|
||||
// 超出maxCount时是否显示查看更多的提示
|
||||
showMore: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.album.showMore
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<view class="u-album">
|
||||
<view
|
||||
class="u-album__row"
|
||||
ref="u-album__row"
|
||||
v-for="(arr, index) in showUrls"
|
||||
:forComputedUse="albumWidth"
|
||||
:key="index"
|
||||
>
|
||||
<view
|
||||
class="u-album__row__wrapper"
|
||||
v-for="(item, index1) in arr"
|
||||
:key="index1"
|
||||
:style="[imageStyle(index + 1, index1 + 1)]"
|
||||
@tap="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
|
||||
>
|
||||
<image
|
||||
:src="getSrc(item)"
|
||||
:mode="
|
||||
urls.length === 1
|
||||
? imageHeight > 0
|
||||
? singleMode
|
||||
: 'widthFix'
|
||||
: multipleMode
|
||||
"
|
||||
:style="[
|
||||
{
|
||||
width: imageWidth,
|
||||
height: imageHeight
|
||||
}
|
||||
]"
|
||||
></image>
|
||||
<view
|
||||
v-if="
|
||||
showMore &&
|
||||
urls.length > rowCount * showUrls.length &&
|
||||
index === showUrls.length - 1 &&
|
||||
index1 === showUrls[showUrls.length - 1].length - 1
|
||||
"
|
||||
class="u-album__row__wrapper__text"
|
||||
>
|
||||
<u--text
|
||||
:text="`+${urls.length - maxCount}`"
|
||||
color="#fff"
|
||||
:size="multipleSize * 0.3"
|
||||
align="center"
|
||||
customStyle="justify-content: center"
|
||||
></u--text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin';
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
|
||||
const dom = uni.requireNativePlugin('dom');
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* Album 相册
|
||||
* @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
|
||||
* @tutorial https://uview.d3u.cn/components/album.html
|
||||
*
|
||||
* @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
|
||||
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
|
||||
* @property {String | Number} singleSize 单图时,图片长边的长度 (默认 180 )
|
||||
* @property {String | Number} multipleSize 多图时,图片边长 (默认 70 )
|
||||
* @property {String | Number} space 多图时,图片水平和垂直之间的间隔 (默认 6 )
|
||||
* @property {String} singleMode 单图时,图片缩放裁剪的模式 (默认 'scaleToFill' )
|
||||
* @property {String} multipleMode 多图时,图片缩放裁剪的模式 (默认 'aspectFill' )
|
||||
* @property {String | Number} maxCount 取消按钮的提示文字 (默认 9 )
|
||||
* @property {Boolean} previewFullImage 是否可以预览图片 (默认 true )
|
||||
* @property {String | Number} rowCount 每行展示图片数量,如设置,singleSize和multipleSize将会无效 (默认 3 )
|
||||
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
|
||||
*
|
||||
* @event {Function} albumWidth 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送 (回调参数 width )
|
||||
* @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-album',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
// 单图的宽度
|
||||
singleWidth: 0,
|
||||
// 单图的高度
|
||||
singleHeight: 0,
|
||||
// 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
|
||||
singlePercent: 0.6,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
urls: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (newVal.length === 1) {
|
||||
this.getImageRect();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
imageStyle() {
|
||||
return (index1, index2) => {
|
||||
const { space, rowCount, multipleSize, urls } = this,
|
||||
{ addUnit, addStyle } = uni.$u,
|
||||
rowLen = this.showUrls.length,
|
||||
allLen = this.urls.length;
|
||||
const style = {
|
||||
marginRight: addUnit(space),
|
||||
marginBottom: addUnit(space),
|
||||
};
|
||||
// 如果为最后一行,则每个图片都无需下边框
|
||||
if (index1 === rowLen) style.marginBottom = 0;
|
||||
// 每行的最右边一张和总长度的最后一张无需右边框
|
||||
if (
|
||||
index2 === rowCount ||
|
||||
(index1 === rowLen &&
|
||||
index2 === this.showUrls[index1 - 1].length)
|
||||
)
|
||||
style.marginRight = 0;
|
||||
return style;
|
||||
};
|
||||
},
|
||||
// 将数组划分为二维数组
|
||||
showUrls() {
|
||||
const arr = [];
|
||||
this.urls.map((item, index) => {
|
||||
// 限制最大展示数量
|
||||
if (index + 1 <= this.maxCount) {
|
||||
// 计算该元素为第几个素组内
|
||||
const itemIndex = Math.floor(index / this.rowCount);
|
||||
// 判断对应的索引是否存在
|
||||
if (!arr[itemIndex]) {
|
||||
arr[itemIndex] = [];
|
||||
}
|
||||
arr[itemIndex].push(item);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
},
|
||||
imageWidth() {
|
||||
return uni.$u.addUnit(
|
||||
this.urls.length === 1
|
||||
? this.singleWidth
|
||||
: this.multipleSize,
|
||||
);
|
||||
},
|
||||
imageHeight() {
|
||||
return uni.$u.addUnit(
|
||||
this.urls.length === 1
|
||||
? this.singleHeight
|
||||
: this.multipleSize,
|
||||
);
|
||||
},
|
||||
// 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
|
||||
// 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
|
||||
albumWidth() {
|
||||
let width = 0;
|
||||
if (this.urls.length === 1) {
|
||||
width = this.singleWidth;
|
||||
} else {
|
||||
width =
|
||||
this.showUrls[0].length * this.multipleSize +
|
||||
this.space * (this.showUrls[0].length - 1);
|
||||
}
|
||||
this.$emit('albumWidth', width);
|
||||
return width;
|
||||
},
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ['albumWidth'],
|
||||
// #endif
|
||||
methods: {
|
||||
// 预览图片
|
||||
onPreviewTap(url) {
|
||||
const urls = this.urls.map((item) => {
|
||||
return this.getSrc(item);
|
||||
});
|
||||
uni.previewImage({
|
||||
current: url,
|
||||
urls,
|
||||
});
|
||||
},
|
||||
// 获取图片的路径
|
||||
getSrc(item) {
|
||||
return uni.$u.test.object(item)
|
||||
? (this.keyName && item[this.keyName]) || item.src
|
||||
: item;
|
||||
},
|
||||
// 单图时,获取图片的尺寸
|
||||
// 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
|
||||
// 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
|
||||
getImageRect() {
|
||||
const src = this.getSrc(this.urls[0]);
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (res) => {
|
||||
let singleSize = uni.$u.getPx(this.singleSize);
|
||||
// 判断图片横向还是竖向展示方式
|
||||
const isHorizotal = res.width >= res.height;
|
||||
this.singleWidth = isHorizotal
|
||||
? singleSize
|
||||
: (res.width / res.height) * singleSize;
|
||||
this.singleHeight = !isHorizotal
|
||||
? singleSize
|
||||
: (res.height / res.width) * this.singleWidth;
|
||||
},
|
||||
fail: () => {
|
||||
this.getComponentWidth();
|
||||
},
|
||||
});
|
||||
},
|
||||
// 获取组件的宽度
|
||||
async getComponentWidth() {
|
||||
// 延时一定时间,以获取dom尺寸
|
||||
await uni.$u.sleep(30);
|
||||
// #ifndef APP-NVUE
|
||||
this.$uGetRect('.u-album__row').then((size) => {
|
||||
this.singleWidth = size.width * this.singlePercent;
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
// 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组
|
||||
const ref = this.$refs['u-album__row'][0];
|
||||
ref &&
|
||||
dom.getComponentRect(ref, (res) => {
|
||||
this.singleWidth = res.size.width * this.singlePercent;
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
.u-album {
|
||||
@include flex(column);
|
||||
|
||||
&__row {
|
||||
@include flex(row);
|
||||
flex-wrap: nowrap;
|
||||
|
||||
&__wrapper {
|
||||
position: relative;
|
||||
|
||||
&__text {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
@include flex(row);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
export default {
|
||||
props: {
|
||||
// 显示文字
|
||||
title: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.alert.title
|
||||
},
|
||||
// 主题,success/warning/info/error
|
||||
type: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.alert.type
|
||||
},
|
||||
// 辅助性文字
|
||||
description: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.alert.description
|
||||
},
|
||||
// 是否可关闭
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.alert.closable
|
||||
},
|
||||
// 是否显示图标
|
||||
showIcon: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.alert.showIcon
|
||||
},
|
||||
// 浅或深色调,light-浅色,dark-深色
|
||||
effect: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.alert.effect
|
||||
},
|
||||
// 文字是否居中
|
||||
center: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.alert.center
|
||||
},
|
||||
// 字体大小
|
||||
fontSize: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.alert.fontSize
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<u-transition
|
||||
mode="fade"
|
||||
:show="show"
|
||||
>
|
||||
<view
|
||||
class="u-alert"
|
||||
:class="[`u-alert--${type}--${effect}`]"
|
||||
@tap.stop="clickHandler"
|
||||
:style="[$u.addStyle(customStyle)]"
|
||||
>
|
||||
<view
|
||||
class="u-alert__icon"
|
||||
v-if="showIcon"
|
||||
>
|
||||
<u-icon
|
||||
:name="iconName"
|
||||
size="18"
|
||||
:color="iconColor"
|
||||
></u-icon>
|
||||
</view>
|
||||
<view
|
||||
class="u-alert__content"
|
||||
:style="[{
|
||||
paddingRight: closable ? '20px' : 0
|
||||
}]"
|
||||
>
|
||||
<text
|
||||
class="u-alert__content__title"
|
||||
v-if="title"
|
||||
:style="[{
|
||||
fontSize: $u.addUnit(fontSize),
|
||||
textAlign: center ? 'center' : 'left'
|
||||
}]"
|
||||
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
|
||||
>{{ title }}</text>
|
||||
<text
|
||||
class="u-alert__content__desc"
|
||||
v-if="description"
|
||||
:style="[{
|
||||
fontSize: $u.addUnit(fontSize),
|
||||
textAlign: center ? 'center' : 'left'
|
||||
}]"
|
||||
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
|
||||
>{{ description }}</text>
|
||||
</view>
|
||||
<view
|
||||
class="u-alert__close"
|
||||
v-if="closable"
|
||||
@tap.stop="closeHandler"
|
||||
>
|
||||
<u-icon
|
||||
name="close"
|
||||
:color="iconColor"
|
||||
size="15"
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</u-transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
|
||||
/**
|
||||
* Alert 警告提示
|
||||
* @description 警告提示,展现需要关注的信息。
|
||||
* @tutorial https://uview.d3u.cn/components/alertTips.html
|
||||
*
|
||||
* @property {String} title 显示的文字
|
||||
* @property {String} type 使用预设的颜色 (默认 'warning' )
|
||||
* @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选
|
||||
* @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false )
|
||||
* @property {Boolean} showIcon 是否显示左边的辅助图标 ( 默认 false )
|
||||
* @property {String} effect 多图时,图片缩放裁剪的模式 (默认 'light' )
|
||||
* @property {Boolean} center 文字是否居中 (默认 false )
|
||||
* @property {String | Number} fontSize 字体大小 (默认 14 )
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
* @event {Function} click 点击组件时触发
|
||||
* @event {Function} close 点击关闭按钮时触发
|
||||
* @example <u-alert :title="title" type = "warning" :closable="closable" :description = "description"></u-alert>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-alert',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
show: true,
|
||||
iconColor:'',
|
||||
iconName:'',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
effect: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.getIconColor()
|
||||
}
|
||||
},
|
||||
type: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.getIconName()
|
||||
}
|
||||
}
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ["click", "close"],
|
||||
// #endif
|
||||
methods: {
|
||||
getIconColor() {
|
||||
this.iconColor = this.effect === 'light' ? this.type : '#fff'
|
||||
},
|
||||
// 不同主题对应不同的图标
|
||||
getIconName() {
|
||||
switch (this.type) {
|
||||
case 'success':
|
||||
this.iconName = 'checkmark-circle-fill';
|
||||
break;
|
||||
case 'error':
|
||||
this.iconName = 'close-circle-fill';
|
||||
break;
|
||||
case 'warning':
|
||||
this.iconName = 'error-circle-fill';
|
||||
break;
|
||||
case 'info':
|
||||
this.iconName = 'info-circle-fill';
|
||||
break;
|
||||
case 'primary':
|
||||
this.iconName = 'more-circle-fill';
|
||||
break;
|
||||
default:
|
||||
this.iconName = 'error-circle-fill';
|
||||
}
|
||||
},
|
||||
// 点击内容
|
||||
clickHandler(e) {
|
||||
this.$emit('click', e)
|
||||
},
|
||||
// 点击关闭按钮
|
||||
closeHandler() {
|
||||
this.show = false
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-alert {
|
||||
position: relative;
|
||||
background-color: $u-primary;
|
||||
padding: 8px 10px;
|
||||
@include flex(row);
|
||||
align-items: center;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
|
||||
&--primary--dark {
|
||||
background-color: $u-primary;
|
||||
}
|
||||
|
||||
&--primary--light {
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
&--error--dark {
|
||||
background-color: $u-error;
|
||||
}
|
||||
|
||||
&--error--light {
|
||||
background-color: #FEF0F0;
|
||||
}
|
||||
|
||||
&--success--dark {
|
||||
background-color: $u-success;
|
||||
}
|
||||
|
||||
&--success--light {
|
||||
background-color: #f5fff0;
|
||||
}
|
||||
|
||||
&--warning--dark {
|
||||
background-color: $u-warning;
|
||||
}
|
||||
|
||||
&--warning--light {
|
||||
background-color: #FDF6EC;
|
||||
}
|
||||
|
||||
&--info--dark {
|
||||
background-color: $u-info;
|
||||
}
|
||||
|
||||
&--info--light {
|
||||
background-color: #f4f4f5;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
@include flex(column);
|
||||
flex: 1;
|
||||
|
||||
&__title {
|
||||
color: $u-main-color;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
color: $u-main-color;
|
||||
font-size: 14px;
|
||||
flex-wrap: wrap;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&__title--dark,
|
||||
&__desc--dark {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
&__text--primary--light,
|
||||
&__text--primary--light {
|
||||
color: $u-primary;
|
||||
}
|
||||
|
||||
&__text--success--light,
|
||||
&__text--success--light {
|
||||
color: $u-success;
|
||||
}
|
||||
|
||||
&__text--warning--light,
|
||||
&__text--warning--light {
|
||||
color: $u-warning;
|
||||
}
|
||||
|
||||
&__text--error--light,
|
||||
&__text--error--light {
|
||||
color: $u-error;
|
||||
}
|
||||
|
||||
&__text--info--light,
|
||||
&__text--info--light {
|
||||
color: $u-info;
|
||||
}
|
||||
|
||||
&__close {
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,100 @@
|
||||
export default {
|
||||
props: {
|
||||
// 金额数值
|
||||
value: {
|
||||
type: Number,
|
||||
default: ()=> uni.$u.props.amount.value
|
||||
},
|
||||
// #ifdef VUE3
|
||||
modelValue: {
|
||||
type: Number,
|
||||
default: ()=> uni.$u.props.amount.value
|
||||
},
|
||||
// #endif
|
||||
// 金融符号
|
||||
symbol: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.amount.symbol
|
||||
},
|
||||
// 是否置后金额符号位置
|
||||
reverse: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.amount.reverse
|
||||
},
|
||||
// 数字精度,小数点后保留几位
|
||||
precision: {
|
||||
type: Number,
|
||||
default: ()=> uni.$u.props.amount.precision
|
||||
},
|
||||
// 数字精度取舍是否四舍五入
|
||||
roundUp: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.amount.roundUp
|
||||
},
|
||||
// 数字变化是否使用动画
|
||||
transition: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.amount.transition
|
||||
},
|
||||
// 数字变化动画时长
|
||||
duration: {
|
||||
type: Number,
|
||||
default: ()=> uni.$u.props.amount.duration
|
||||
},
|
||||
// 分隔符位置 (3为千分位,4为万分位)
|
||||
separatorDigits: {
|
||||
type: Number,
|
||||
default: ()=> uni.$u.props.amount.separatorDigits
|
||||
},
|
||||
// 分隔符
|
||||
separator: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.amount.separator
|
||||
},
|
||||
// 是否显示金融符号
|
||||
showSymbol: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.amount.showSymbol
|
||||
},
|
||||
// 是否显示小数
|
||||
showDecimal: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.amount.showDecimal
|
||||
},
|
||||
// 是否显示分隔符
|
||||
showSeparator: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.amount.showSeparator
|
||||
},
|
||||
// 数字是否转换为大写中文
|
||||
capital: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.amount.capital
|
||||
},
|
||||
// 整数字体大小
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.amount.fontSize
|
||||
},
|
||||
// 金融符号小数与整数的比例或字体大小
|
||||
fontSizeRatio: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.amount.fontSizeRatio
|
||||
},
|
||||
// 颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.amount.color
|
||||
},
|
||||
// 金融符号与小数字体颜色
|
||||
colorRatio: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.amount.colorRatio
|
||||
},
|
||||
// 自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default: ()=> uni.$u.props.amount.customStyle
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
<template>
|
||||
<view class="u-amount" :style="[$u.addStyle(customStyle)]">
|
||||
<!-- 插槽模式 -->
|
||||
<slot v-if="$slots.default || $slots.$default" :integer="integerPart" :decimal="decimalPart" :capital="capitalText"></slot>
|
||||
<!-- 默认显示模式 -->
|
||||
<template v-else>
|
||||
<!-- 大写中文模式 -->
|
||||
<text v-if="capital" class="u-amount__capital" :style="[textStyle]">{{ capitalText }}</text>
|
||||
<!-- 普通数字模式 -->
|
||||
<template v-else>
|
||||
<!-- 前置符号 -->
|
||||
<text v-if="showSymbol && !reverse" class="u-amount__symbol" :style="[symbolStyle]">{{ symbol }}</text>
|
||||
<!-- 整数部分 -->
|
||||
<text class="u-amount__integer" :style="[integerStyle]">{{ formattedInteger }}</text>
|
||||
<!-- 小数部分 -->
|
||||
<text v-if="showDecimal && decimalPart" class="u-amount__decimal" :style="[decimalStyle]">.{{ decimalPart }}</text>
|
||||
<!-- 后置符号 -->
|
||||
<text v-if="showSymbol && reverse" class="u-amount__symbol" :style="[symbolStyle]">{{ symbol }}</text>
|
||||
</template>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
|
||||
/**
|
||||
* amount 金融数字
|
||||
* @description 用于展示金额、价格等,可以转换为人民币大写,支持动画效果
|
||||
* @tutorial https://uviewui.com/components/amount.html
|
||||
*
|
||||
* @property {Number} v-model/modelValue 金额数值 (默认 0 )
|
||||
* @property {String} symbol 金融符号 (默认 '¥' )
|
||||
* @property {Boolean} reverse 是否置后金额符号位置 (默认 false )
|
||||
* @property {Number} precision 数字精度,小数点后保留几位 (默认 2 )
|
||||
* @property {Boolean} roundUp 数字精度取舍是否四舍五入 (默认 true )
|
||||
* @property {Boolean} transition 数字变化是否使用动画 (默认 false )
|
||||
* @property {Number} duration 数字变化动画时长 (默认 1000 )
|
||||
* @property {Number} separatorDigits 分隔符位置 (默认 4 )
|
||||
* @property {String} separator 分隔符 (默认 ',' )
|
||||
* @property {Boolean} showSymbol 是否显示金融符号 (默认 true )
|
||||
* @property {Boolean} showDecimal 是否显示小数 (默认 true )
|
||||
* @property {Boolean} showSeparator 是否显示分隔符 (默认 false )
|
||||
* @property {Boolean} capital 数字是否转换为大写中文 (默认 false )
|
||||
* @property {String} fontSize 整数字体大小 (默认 '' )
|
||||
* @property {String|Number} fontSizeRatio 金融符号小数字体大小 (默认 0.7 )
|
||||
* @property {String} color 颜色 (默认 '' )
|
||||
* @property {String} colorRatio 金融符号与小数字体颜色
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
* @example <u-amount v-model="123456.789" :show-separator="true" />
|
||||
*/
|
||||
export default {
|
||||
name: 'u-amount',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
displayValue: 0,
|
||||
animationTimer: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 格式化后的数值
|
||||
formattedValue() {
|
||||
let num = this.displayValue;
|
||||
if (this.roundUp) {
|
||||
num = Math.round(num * Math.pow(10, this.precision)) / Math.pow(10, this.precision);
|
||||
} else {
|
||||
num = Math.floor(num * Math.pow(10, this.precision)) / Math.pow(10, this.precision);
|
||||
}
|
||||
return num.toFixed(this.precision);
|
||||
},
|
||||
|
||||
// 整数部分
|
||||
integerPart() {
|
||||
const parts = this.formattedValue.split('.');
|
||||
return parts[0];
|
||||
},
|
||||
|
||||
// 小数部分
|
||||
decimalPart() {
|
||||
const parts = this.formattedValue.split('.');
|
||||
return parts[1] || '';
|
||||
},
|
||||
|
||||
// 格式化的整数(带分隔符)
|
||||
formattedInteger() {
|
||||
if (!this.showSeparator) return this.integerPart;
|
||||
return this.addSeparator(this.integerPart);
|
||||
},
|
||||
|
||||
// 中文大写
|
||||
capitalText() {
|
||||
if (!this.capital) return '';
|
||||
return this.toChinese(this.displayValue);
|
||||
},
|
||||
|
||||
// 文本通用样式
|
||||
textStyle() {
|
||||
const style = {};
|
||||
if (this.color){
|
||||
style.color = this.color;
|
||||
}
|
||||
if (this.fontSize){
|
||||
style.fontSize = this.fontSize;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
|
||||
// 符号样式
|
||||
symbolStyle() {
|
||||
const style = {};
|
||||
if (this.color){
|
||||
style.color = this.color;
|
||||
}
|
||||
if (this.colorRatio){
|
||||
style.color = this.colorRatio;
|
||||
}
|
||||
if (this.fontSize) {
|
||||
if (this.fontSizeRatio) {
|
||||
style.fontSize = this.fontSizeRatio;
|
||||
} else {
|
||||
const size = parseFloat(this.fontSize);
|
||||
const ratio = parseFloat(this.fontSizeRatio);
|
||||
style.fontSize = uni.$u.addUnit(size * ratio);
|
||||
}
|
||||
}
|
||||
return style;
|
||||
},
|
||||
|
||||
// 整数样式
|
||||
integerStyle() {
|
||||
const style = {};
|
||||
if (this.color){
|
||||
style.color = this.color;
|
||||
}
|
||||
if (this.fontSize){
|
||||
style.fontSize = this.fontSize;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
|
||||
// 小数样式
|
||||
decimalStyle() {
|
||||
const style = {};
|
||||
if (this.color){
|
||||
style.color = this.color;
|
||||
}
|
||||
if (this.colorRatio){
|
||||
style.color = this.colorRatio;
|
||||
}
|
||||
if (this.fontSize) {
|
||||
if (this.fontSizeRatio) {
|
||||
style.fontSize = this.fontSizeRatio;
|
||||
} else {
|
||||
const size = parseFloat(this.fontSize);
|
||||
const ratio = parseFloat(this.fontSizeRatio);
|
||||
style.fontSize = uni.$u.addUnit((size * ratio));
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(newVal) {
|
||||
if(!newVal) return;
|
||||
if (this.transition) {
|
||||
this.startAnimation(newVal);
|
||||
} else {
|
||||
this.displayValue = newVal;
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
// #ifdef VUE3
|
||||
modelValue: {
|
||||
handler(newVal) {
|
||||
if(!newVal) return;
|
||||
if (this.transition) {
|
||||
this.startAnimation(newVal);
|
||||
} else {
|
||||
this.displayValue = newVal;
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
// 添加分隔符
|
||||
addSeparator(num) {
|
||||
const str = num.toString();
|
||||
const len = str.length;
|
||||
let result = '';
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (i > 0 && (len - i) % this.separatorDigits === 0) {
|
||||
result += this.separator;
|
||||
}
|
||||
result += str[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// 启动动画
|
||||
startAnimation(targetValue) {
|
||||
if (this.animationTimer) {
|
||||
clearInterval(this.animationTimer);
|
||||
}
|
||||
|
||||
const startValue = this.displayValue;
|
||||
const diff = targetValue - startValue;
|
||||
const stepTime = 16; // 60fps
|
||||
const steps = this.duration / stepTime;
|
||||
const stepValue = diff / steps;
|
||||
let currentStep = 0;
|
||||
|
||||
this.animationTimer = setInterval(() => {
|
||||
currentStep++;
|
||||
if (currentStep >= steps) {
|
||||
this.displayValue = targetValue;
|
||||
clearInterval(this.animationTimer);
|
||||
this.animationTimer = null;
|
||||
} else {
|
||||
this.displayValue = startValue + (stepValue * currentStep);
|
||||
}
|
||||
}, stepTime);
|
||||
},
|
||||
|
||||
// 转换为中文大写
|
||||
toChinese(amount) {
|
||||
// 小数单位:角、分、毫、厘
|
||||
const fractionUnits = ['角', '分', '毫', '厘'];
|
||||
// 数字对应的中文大写
|
||||
const chineseDigits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||
// 整数单位
|
||||
const integerUnits = [
|
||||
['元', '万', '亿'],
|
||||
['', '拾', '佰', '仟']
|
||||
];
|
||||
|
||||
// 处理负数
|
||||
const prefix = amount < 0 ? '欠' : '';
|
||||
amount = Math.abs(amount);
|
||||
|
||||
let result = '';
|
||||
|
||||
// 处理小数部分(角、分、毫、厘)
|
||||
for (let i = 0; i < fractionUnits.length; i++) {
|
||||
const decimalDigit = Math.floor(amount * Math.pow(10, i + 1)) % 10;
|
||||
if (decimalDigit > 0) {
|
||||
result += chineseDigits[decimalDigit] + fractionUnits[i];
|
||||
}
|
||||
}
|
||||
|
||||
// 如果小数部分为空,则显示"整"
|
||||
result = result || '整';
|
||||
|
||||
// 处理整数部分
|
||||
let integerPart = Math.floor(amount);
|
||||
|
||||
for (let i = 0; i < integerUnits[0].length && integerPart > 0; i++) {
|
||||
let sectionResult = '';
|
||||
for (let j = 0; j < integerUnits[1].length && integerPart > 0; j++) {
|
||||
sectionResult = chineseDigits[integerPart % 10] + integerUnits[1][j] + sectionResult;
|
||||
integerPart = Math.floor(integerPart / 10);
|
||||
}
|
||||
result = sectionResult.replace(/(零.)*零$/, '').replace(/^$/, '零') + integerUnits[0][i] + result;
|
||||
}
|
||||
|
||||
// 处理特殊情况和格式化
|
||||
return prefix + result.replace(/(零.)*零元/, '元')
|
||||
.replace(/(零.)+/g, '零')
|
||||
.replace(/^整$/, '零元整')
|
||||
.replace(/零角零分零毫零厘/, '整')
|
||||
.replace(/零角零分零毫/, '')
|
||||
.replace(/零角零分/, '')
|
||||
.replace(/零角/, '');
|
||||
}
|
||||
|
||||
},
|
||||
// #ifdef VUE2
|
||||
beforeDestroy() {
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
beforeUnmount() {
|
||||
// #endif
|
||||
if (this.animationTimer) {
|
||||
clearInterval(this.animationTimer);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-amount {
|
||||
@include flex(row);
|
||||
align-items: baseline;
|
||||
|
||||
&__symbol {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&__integer {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__decimal {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&__capital {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
export default {
|
||||
props: {
|
||||
// 输入的值
|
||||
// #ifdef VUE2
|
||||
value: {
|
||||
type: [Array, String, Number],
|
||||
default: ()=> uni.$u.props.areaPicker.value
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
modelValue: {
|
||||
type: [Array, String, Number],
|
||||
default: ()=> uni.$u.props.areaPicker.value
|
||||
},
|
||||
// #endif
|
||||
// 是否展示picker弹窗
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.show
|
||||
},
|
||||
// 是否显示省份
|
||||
province: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.province
|
||||
},
|
||||
// 是否显示城市
|
||||
city: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.city
|
||||
},
|
||||
// 是否显示区县
|
||||
county: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.county
|
||||
},
|
||||
// 是否展示顶部的操作栏
|
||||
showToolbar: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.showToolbar
|
||||
},
|
||||
// 顶部标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.areaPicker.title
|
||||
},
|
||||
// 是否显示加载中状态
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.loading
|
||||
},
|
||||
// 各列中,单个选项的高度
|
||||
itemHeight: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.areaPicker.itemHeight
|
||||
},
|
||||
// 取消按钮的文字
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.$t('uAreaPicker.cancelText')
|
||||
},
|
||||
// 确认按钮的文字
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.$t('uAreaPicker.confirmText')
|
||||
},
|
||||
// 取消按钮的颜色
|
||||
cancelColor: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.areaPicker.cancelColor
|
||||
},
|
||||
// 确认按钮的颜色
|
||||
confirmColor: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.areaPicker.confirmColor
|
||||
},
|
||||
// 每列中可见选项的数量
|
||||
visibleItemCount: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.areaPicker.visibleItemCount
|
||||
},
|
||||
// 是否允许点击遮罩关闭选择器
|
||||
closeOnClickOverlay: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.closeOnClickOverlay
|
||||
},
|
||||
// 是否在手指松开时立即触发 change 事件。若不开启则会在滚动动画结束后触发 change 事件,只在微信2.21.1及以上有效
|
||||
immediateChange: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.immediateChange
|
||||
},
|
||||
// 设置圆角值
|
||||
round: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.areaPicker.round
|
||||
},
|
||||
showInput: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.areaPicker.showInput
|
||||
},
|
||||
inputProps:{
|
||||
type: Object,
|
||||
default: ()=> uni.$u.props.areaPicker.inputProps
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<u-picker
|
||||
ref="areaPicker"
|
||||
keyName="label"
|
||||
valueName="value"
|
||||
:show="show"
|
||||
:columns="columns"
|
||||
:showToolbar="showToolbar"
|
||||
:title="title"
|
||||
:loading="loading"
|
||||
:itemHeight="itemHeight"
|
||||
:cancelText="cancelText"
|
||||
:confirmText="confirmText"
|
||||
:cancelColor="cancelColor"
|
||||
:confirmColor="confirmColor"
|
||||
:visibleItemCount="visibleItemCount"
|
||||
:closeOnClickOverlay="closeOnClickOverlay"
|
||||
:defaultIndex="innerDefaultIndex"
|
||||
:immediateChange="immediateChange"
|
||||
:round="round"
|
||||
@close="close"
|
||||
@cancel="cancel"
|
||||
@confirm="confirm"
|
||||
@change="change"
|
||||
>
|
||||
<template v-slot:trigger>
|
||||
<slot name="trigger">
|
||||
<u-input
|
||||
v-if="showInput"
|
||||
:value="inputValue"
|
||||
:clearable="inputProps.clearable"
|
||||
:placeholder="inputProps.placeholder"
|
||||
:disabled="inputProps.disabled"
|
||||
:border="inputProps.border"
|
||||
:round="inputProps.round"
|
||||
:backgroundColor="inputProps.disabled ? '' : inputProps.backgroundColor"
|
||||
:disabledColor="inputProps.disabled ? inputProps.backgroundColor : ''"
|
||||
:placeholderClass="inputProps.placeholderClass"
|
||||
:placeholderStyle="inputProps.placeholderStyle"
|
||||
:confirmType="inputProps.confirmType"
|
||||
:focus="inputProps.focus"
|
||||
:inputAlign="inputProps.inputAlign"
|
||||
:fontSize="inputProps.fontSize"
|
||||
:color="inputProps.color"
|
||||
:borderColor="inputProps.borderColor"
|
||||
:prefixIcon="inputProps.prefixIcon"
|
||||
:suffixIcon="inputProps.suffixIcon"
|
||||
:suffixIconStyle="inputProps.suffixIconStyle"
|
||||
:prefixIconStyle="inputProps.prefixIconStyle"
|
||||
:shape="inputProps.shape"
|
||||
:customStyle="inputProps.customStyle"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
</u-picker>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import area from '../../libs/util/area.js'
|
||||
import props from './props.js'
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin'
|
||||
|
||||
export default {
|
||||
name: 'u-region-picker',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
inputValue: '',
|
||||
columns: [],
|
||||
innerDefaultIndex: [],
|
||||
selectedValue:[]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// #ifdef VUE2
|
||||
value(newValue) {
|
||||
if(newValue){
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
modelValue(newValue) {
|
||||
if(newValue){
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ['update:modelValue','init', 'confirm', 'change', 'close', 'cancel'],
|
||||
// #endif
|
||||
methods: {
|
||||
// 初始化数据
|
||||
init() {
|
||||
const columns = [];
|
||||
const innerDefaultIndex = [];
|
||||
const innerDefaultLabel = []
|
||||
|
||||
// 获取当前值
|
||||
const currentValue = this.getCurrentValue();
|
||||
|
||||
// 处理省份列
|
||||
let selectedProvince;
|
||||
if (this.province) {
|
||||
const province = this.getProvinceList();
|
||||
columns.push(province);
|
||||
|
||||
selectedProvince = this.getSelectedValue(0, province);
|
||||
let provinceIndex = province.findIndex(item => item.value == selectedProvince);
|
||||
innerDefaultIndex.push(provinceIndex >= 0 ? provinceIndex : 0);
|
||||
innerDefaultLabel.push(province[provinceIndex].label)
|
||||
}
|
||||
|
||||
// 处理城市列
|
||||
let selectedCity;
|
||||
if (this.city) {
|
||||
const city = this.getCityList(selectedProvince);
|
||||
columns.push(city);
|
||||
|
||||
const cityIndex = this.province ? 1 : 0;
|
||||
selectedCity = this.getSelectedValue(cityIndex, city);
|
||||
let cityIndexValue = city.findIndex(item => item.value == selectedCity);
|
||||
innerDefaultIndex.push(cityIndexValue >= 0 ? cityIndexValue : 0);
|
||||
innerDefaultLabel.push(city[cityIndexValue].label)
|
||||
}
|
||||
|
||||
// 处理县区列
|
||||
if (this.county && selectedCity) {
|
||||
const county = this.getCountyList(selectedCity);
|
||||
columns.push(county);
|
||||
|
||||
const countyIndex = (this.province ? 1 : 0) + (this.city ? 1 : 0);
|
||||
const selectedCounty = this.getSelectedValue(countyIndex, county);
|
||||
let countyIndexValue = county.findIndex(item => item.value == selectedCounty);
|
||||
innerDefaultIndex.push(countyIndexValue >= 0 ? countyIndexValue : 0);
|
||||
innerDefaultLabel.push(county[countyIndexValue].label)
|
||||
}
|
||||
|
||||
// 设置列数据
|
||||
this.columns = columns;
|
||||
this.innerDefaultIndex = innerDefaultIndex;
|
||||
if(this.showInput && currentValue.length > 0){
|
||||
this.inputValue = innerDefaultLabel.join(this.inputProps.separator)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// 获取选中值
|
||||
getSelectedValue(index, list) {
|
||||
const currentValue = this.getCurrentValue();
|
||||
// 如果有默认值且列表不为空,则使用默认值,否则使用第一个值
|
||||
return (currentValue && currentValue[index] && list.length > 0)
|
||||
? currentValue[index]
|
||||
: list[0]?.value;
|
||||
},
|
||||
|
||||
// 获取当前值,兼容 Vue2 和 Vue3
|
||||
getCurrentValue() {
|
||||
// #ifdef VUE2
|
||||
return this.value;
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
return this.modelValue;
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 获取省份数据
|
||||
getProvinceList() {
|
||||
let list = [];
|
||||
Object.entries(area.province_list).forEach(([value, label],idx) => {
|
||||
list.push({ value, label });
|
||||
});
|
||||
return list;
|
||||
},
|
||||
|
||||
// 获取城市数据
|
||||
getCityList(cityCode) {
|
||||
let list = [];
|
||||
Object.entries(area.city_list).forEach(([value, label],idx) => {
|
||||
if(cityCode){
|
||||
if (value.startsWith(String(cityCode).substring(0, 2))) {
|
||||
list.push({ value, label });
|
||||
}
|
||||
}else{
|
||||
list.push({ value, label });
|
||||
}
|
||||
});
|
||||
return list;
|
||||
},
|
||||
|
||||
// 获取区县数据
|
||||
getCountyList(countyCode) {
|
||||
let list = [];
|
||||
Object.entries(area.county_list).forEach(([value, label],idx) => {
|
||||
if(countyCode){
|
||||
if (value.startsWith(String(countyCode).substring(0, 4))) {
|
||||
list.push({ value, label });
|
||||
}
|
||||
}else{
|
||||
list.push({ value, label });
|
||||
}
|
||||
});
|
||||
return list;
|
||||
},
|
||||
|
||||
// 选择器变化事件
|
||||
change(e) {
|
||||
const { columnIndex, value } = e;
|
||||
|
||||
// 计算实际的列索引(考虑可能跳过的列)
|
||||
let actualColumnIndex = columnIndex;
|
||||
let valueIndex = columnIndex;
|
||||
|
||||
// 省份列变化
|
||||
if ((this.province && actualColumnIndex === 0) && this.city) {
|
||||
// 选择省份时更新城市列表
|
||||
const cityList = this.getCityList(value[valueIndex].value);
|
||||
const cityColumnIndex = this.province ? 1 : 0;
|
||||
this.$refs.areaPicker.setColumnValues(cityColumnIndex, cityList);
|
||||
|
||||
// 如果显示区县列,同时更新区县列表
|
||||
if (this.county && cityList.length > 0) {
|
||||
const countyList = this.getCountyList(cityList[0].value);
|
||||
const countyColumnIndex = (this.province ? 1 : 0) + (this.city ? 1 : 0);
|
||||
this.$refs.areaPicker.setColumnValues(countyColumnIndex, countyList);
|
||||
}
|
||||
}
|
||||
// 城市列变化
|
||||
else if (this.city &&
|
||||
((this.province && actualColumnIndex === 1) ||
|
||||
(!this.province && actualColumnIndex === 0)) &&
|
||||
this.county) {
|
||||
// 选择城市时更新区县列表
|
||||
const cityValueIndex = this.province ? 1 : 0;
|
||||
let countyList = this.getCountyList(value[cityValueIndex].value);
|
||||
const countyColumnIndex = (this.province ? 1 : 0) + (this.city ? 1 : 0) - 1;
|
||||
this.$refs.areaPicker.setColumnValues(countyColumnIndex + 1, countyList);
|
||||
}
|
||||
|
||||
this.$emit('change', e);
|
||||
},
|
||||
// 关闭选择器
|
||||
close() {
|
||||
if (this.closeOnClickOverlay) {
|
||||
this.$emit('close')
|
||||
}
|
||||
},
|
||||
// 确认选择
|
||||
confirm(e) {
|
||||
this.$emit('confirm', e);
|
||||
let value = e.value.map(item => item.value)
|
||||
|
||||
if(this.showInput){
|
||||
let label = e.value.map(item => item.label)
|
||||
this.inputValue = label.join(this.inputProps.separator)
|
||||
}
|
||||
|
||||
// #ifdef VUE2
|
||||
this.$emit('input', value)
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
this.$emit('update:modelValue', value)
|
||||
// #endif
|
||||
},
|
||||
// 取消选择
|
||||
cancel() {
|
||||
this.$emit('cancel');
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
export default {
|
||||
props: {
|
||||
// 头像图片组
|
||||
urls: {
|
||||
type: Array,
|
||||
default: ()=> uni.$u.props.avatarGroup.urls
|
||||
},
|
||||
// 最多展示的头像数量
|
||||
maxCount: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.avatarGroup.maxCount
|
||||
},
|
||||
// 头像形状
|
||||
shape: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatarGroup.shape
|
||||
},
|
||||
// 图片裁剪模式
|
||||
mode: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatarGroup.mode
|
||||
},
|
||||
// 超出maxCount时是否显示查看更多的提示
|
||||
showMore: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.avatarGroup.showMore
|
||||
},
|
||||
// 头像大小
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.avatarGroup.size
|
||||
},
|
||||
// 指定从数组的对象元素中读取哪个属性作为图片地址
|
||||
keyName: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatarGroup.keyName
|
||||
},
|
||||
// 头像之间的遮挡比例
|
||||
gap: {
|
||||
type: [String, Number],
|
||||
validator(value) {
|
||||
return value >= 0 && value <= 1
|
||||
},
|
||||
default: ()=> uni.$u.props.avatarGroup.gap
|
||||
},
|
||||
// 需额外显示的值
|
||||
extraValue: {
|
||||
type: [Number, String],
|
||||
default: ()=> uni.$u.props.avatarGroup.extraValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<view class="u-avatar-group">
|
||||
<view
|
||||
class="u-avatar-group__item"
|
||||
v-for="(item, index) in showUrl"
|
||||
:key="index"
|
||||
:style="{
|
||||
marginLeft: index === 0 ? 0 : $u.addUnit(-size * gap)
|
||||
}"
|
||||
>
|
||||
<u-avatar
|
||||
:size="size"
|
||||
:shape="shape"
|
||||
:mode="mode"
|
||||
:src="$u.test.object(item) ? keyName && item[keyName] || item.url : item"
|
||||
></u-avatar>
|
||||
<view
|
||||
class="u-avatar-group__item__show-more"
|
||||
v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
|
||||
@tap="clickHandler"
|
||||
>
|
||||
<u--text
|
||||
color="#ffffff"
|
||||
:size="size * 0.4"
|
||||
:text="`+${extraValue || urls.length - showUrl.length}`"
|
||||
align="center"
|
||||
customStyle="justify-content: center"
|
||||
></u--text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
/**
|
||||
* AvatarGroup 头像组
|
||||
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
|
||||
* @tutorial https://uview.d3u.cn/components/avatar.html
|
||||
*
|
||||
* @property {Array} urls 头像图片组 (默认 [] )
|
||||
* @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
|
||||
* @property {String} shape 头像形状( 'circle' (默认) | 'square' )
|
||||
* @property {String} mode 图片裁剪模式(默认 'scaleToFill' )
|
||||
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
|
||||
* @property {String | Number} size 头像大小 (默认 40 )
|
||||
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
|
||||
* @property {String | Number} gap 头像之间的遮挡比例(0.4代表遮挡40%) (默认 0.5 )
|
||||
* @property {String | Number} extraValue 需额外显示的值
|
||||
* @event {Function} showMore 头像组更多点击
|
||||
* @example <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-avatar-group',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showUrl() {
|
||||
return this.urls.slice(0, this.maxCount)
|
||||
}
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ["showMore"],
|
||||
// #endif
|
||||
methods: {
|
||||
clickHandler() {
|
||||
this.$emit('showMore')
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-avatar-group {
|
||||
@include flex;
|
||||
|
||||
&__item {
|
||||
margin-left: -10px;
|
||||
position: relative;
|
||||
|
||||
&--no-indent {
|
||||
// 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&__show-more {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
@include flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
export default {
|
||||
props: {
|
||||
// 头像图片路径(不能为相对路径)
|
||||
src: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.src
|
||||
},
|
||||
// 头像形状,circle-圆形,square-方形
|
||||
shape: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.shape
|
||||
},
|
||||
// 头像尺寸
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.avatar.size
|
||||
},
|
||||
// 裁剪模式
|
||||
mode: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.mode
|
||||
},
|
||||
// 显示的文字
|
||||
text: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.text
|
||||
},
|
||||
// 背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.bgColor
|
||||
},
|
||||
// 文字颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.color
|
||||
},
|
||||
// 文字大小
|
||||
fontSize: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.avatar.fontSize
|
||||
},
|
||||
// 显示的图标
|
||||
icon: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.icon
|
||||
},
|
||||
// 显示小程序头像,只对百度,微信,QQ小程序有效
|
||||
mpAvatar: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.avatar.mpAvatar
|
||||
},
|
||||
// 是否使用随机背景色
|
||||
randomBgColor: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.avatar.randomBgColor
|
||||
},
|
||||
// 加载失败的默认头像(组件有内置默认图片)
|
||||
defaultUrl: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.defaultUrl
|
||||
},
|
||||
// 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
|
||||
colorIndex: {
|
||||
type: [String, Number],
|
||||
// 校验参数规则,索引在0-19之间
|
||||
validator(n) {
|
||||
return uni.$u.test.range(n, [0, 19]) || n === ''
|
||||
},
|
||||
default: ()=> uni.$u.props.avatar.colorIndex
|
||||
},
|
||||
// 组件标识符
|
||||
name: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.avatar.name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<view
|
||||
class="u-avatar"
|
||||
:class="[`u-avatar--${shape}`]"
|
||||
:style="[{
|
||||
backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $u.random(0, 19)] : bgColor) : 'transparent',
|
||||
width: $u.addUnit(size),
|
||||
height: $u.addUnit(size),
|
||||
}, $u.addStyle(customStyle)]"
|
||||
@tap="clickHandler"
|
||||
>
|
||||
<slot>
|
||||
<!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU -->
|
||||
<open-data
|
||||
v-if="mpAvatar && allowMp"
|
||||
type="userAvatarUrl"
|
||||
:style="[{
|
||||
width: $u.addUnit(size),
|
||||
height: $u.addUnit(size)
|
||||
}]"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
|
||||
<template v-if="mpAvatar && allowMp"></template>
|
||||
<!-- #endif -->
|
||||
<u-icon
|
||||
v-else-if="icon"
|
||||
:name="icon"
|
||||
:size="fontSize"
|
||||
:color="color"
|
||||
></u-icon>
|
||||
<u--text
|
||||
v-else-if="text"
|
||||
:text="text"
|
||||
:size="fontSize"
|
||||
:color="color"
|
||||
align="center"
|
||||
customStyle="justify-content: center"
|
||||
></u--text>
|
||||
<image
|
||||
class="u-avatar__image"
|
||||
v-else
|
||||
:class="[`u-avatar__image--${shape}`]"
|
||||
:src="avatarUrl || defaultUrl"
|
||||
:mode="mode"
|
||||
@error="errorHandler"
|
||||
:style="[{
|
||||
width: $u.addUnit(size),
|
||||
height: $u.addUnit(size)
|
||||
}]"
|
||||
></image>
|
||||
</slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
const base64Avatar =
|
||||
"data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z";
|
||||
/**
|
||||
* Avatar 头像
|
||||
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
|
||||
* @tutorial https://uview.d3u.cn/components/avatar.html
|
||||
*
|
||||
* @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径)
|
||||
* @property {String} shape 头像形状 ( circle (默认) | square)
|
||||
* @property {String | Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值 (默认 40 )
|
||||
* @property {String} mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值 (默认 'scaleToFill' )
|
||||
* @property {String} text 用文字替代图片,级别优先于src
|
||||
* @property {String} bgColor 背景颜色,一般显示文字时用 (默认 '#c0c4cc' )
|
||||
* @property {String} color 文字颜色 (默认 '#ffffff' )
|
||||
* @property {String | Number} fontSize 文字大小 (默认 18 )
|
||||
* @property {String} icon 显示的图标
|
||||
* @property {Boolean} mpAvatar 显示小程序头像,只对百度,微信,QQ小程序有效 (默认 false )
|
||||
* @property {Boolean} randomBgColor 是否使用随机背景色 (默认 false )
|
||||
* @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片)
|
||||
* @property {String | Number} colorIndex 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
|
||||
* @property {String} name 组件标识符 (默认 'level' )
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
*
|
||||
* @event {Function} click 点击组件时触发 index: 用户传递的标识符
|
||||
* @example <u-avatar :src="src" mode="square"></u-avatar>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-avatar',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
// 如果配置randomBgColor参数为true,在图标或者文字的模式下,会随机从中取出一个颜色值当做背景色
|
||||
colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2',
|
||||
'#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee',
|
||||
'#73d1f1',
|
||||
'#80a7dc'
|
||||
],
|
||||
avatarUrl: this.src,
|
||||
allowMp: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听头像src的变化,赋值给内部的avatarUrl变量,因为图片加载失败时,需要修改图片的src为默认值
|
||||
// 而组件内部不能直接修改props的值,所以需要一个中间变量
|
||||
src: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.avatarUrl = newVal
|
||||
// 如果没有传src,则主动触发error事件,用于显示默认的头像,否则src为''空字符等的时候,会无内容展示
|
||||
if(!newVal) {
|
||||
this.errorHandler()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
imageStyle() {
|
||||
const style = {}
|
||||
return style
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ["click"],
|
||||
// #endif
|
||||
methods: {
|
||||
init() {
|
||||
// 目前只有这几个小程序平台具有open-data标签
|
||||
// 其他平台可以通过uni.getUserInfo类似接口获取信息,但是需要弹窗授权(首次),不合符组件逻辑
|
||||
// 故目前自动获取小程序头像只支持这几个平台
|
||||
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
|
||||
this.allowMp = true
|
||||
// #endif
|
||||
},
|
||||
// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
|
||||
isImg() {
|
||||
return this.src.indexOf('/') !== -1
|
||||
},
|
||||
// 图片加载时失败时触发
|
||||
errorHandler() {
|
||||
this.avatarUrl = this.defaultUrl || base64Avatar
|
||||
},
|
||||
clickHandler() {
|
||||
this.$emit('click', this.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-avatar {
|
||||
@include flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&--circle {
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
&--square {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&__image {
|
||||
&--circle {
|
||||
border-radius: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&--square {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,54 @@
|
||||
export default {
|
||||
props: {
|
||||
// 返回顶部的形状,circle-圆形,square-方形
|
||||
mode: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.backtop.mode
|
||||
},
|
||||
// 自定义图标
|
||||
icon: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.backtop.icon
|
||||
},
|
||||
// 提示文字
|
||||
text: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.backtop.text
|
||||
},
|
||||
// 返回顶部滚动时间
|
||||
duration: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.backtop.duration
|
||||
},
|
||||
// 滚动距离
|
||||
scrollTop: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.backtop.scrollTop
|
||||
},
|
||||
// 距离顶部多少距离显示,单位px
|
||||
top: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.backtop.top
|
||||
},
|
||||
// 返回顶部按钮到底部的距离,单位px
|
||||
bottom: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.backtop.bottom
|
||||
},
|
||||
// 返回顶部按钮到右边的距离,单位px
|
||||
right: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.backtop.right
|
||||
},
|
||||
// 层级
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.backtop.zIndex
|
||||
},
|
||||
// 图标的样式,对象形式
|
||||
iconStyle: {
|
||||
type: Object,
|
||||
default: ()=> uni.$u.props.backtop.iconStyle
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<u-transition
|
||||
mode="fade"
|
||||
:customStyle="backTopStyle"
|
||||
:show="show"
|
||||
>
|
||||
<view
|
||||
class="u-back-top"
|
||||
:style="[contentStyle]"
|
||||
v-if="!$slots.default && !$slots.$default"
|
||||
@click="backToTop"
|
||||
>
|
||||
<u-icon
|
||||
:name="icon"
|
||||
:custom-style="iconStyle"
|
||||
></u-icon>
|
||||
<text
|
||||
v-if="text"
|
||||
class="u-back-top__text"
|
||||
>{{text}}</text>
|
||||
</view>
|
||||
<slot v-else />
|
||||
</u-transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
// #ifdef APP-NVUE
|
||||
const dom = weex.requireModule('dom')
|
||||
// #endif
|
||||
/**
|
||||
* backTop 返回顶部
|
||||
* @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
|
||||
* @tutorial https://uviewui.com/components/backTop.html
|
||||
*
|
||||
* @property {String} mode 返回顶部的形状,circle-圆形,square-方形 (默认 'circle' )
|
||||
* @property {String} icon 自定义图标 (默认 'arrow-upward' ) 见官方文档示例
|
||||
* @property {String} text 提示文字
|
||||
* @property {String | Number} duration 返回顶部滚动时间 (默认 100)
|
||||
* @property {String | Number} scrollTop 滚动距离 (默认 0 )
|
||||
* @property {String | Number} top 距离顶部多少距离显示,单位px (默认 400 )
|
||||
* @property {String | Number} bottom 返回顶部按钮到底部的距离,单位px (默认 100 )
|
||||
* @property {String | Number} right 返回顶部按钮到右边的距离,单位px (默认 20 )
|
||||
* @property {String | Number} zIndex 层级 (默认 9 )
|
||||
* @property {Object<Object>} iconStyle 图标的样式,对象形式 (默认 {color: '#909399',fontSize: '19px'})
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
*
|
||||
* @example <u-back-top :scrollTop="scrollTop"></u-back-top>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-back-top',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
computed: {
|
||||
backTopStyle() {
|
||||
// 动画组件样式
|
||||
const style = {
|
||||
bottom: uni.$u.addUnit(this.bottom),
|
||||
right: uni.$u.addUnit(this.right),
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
position: 'fixed',
|
||||
zIndex: 10,
|
||||
}
|
||||
return style
|
||||
},
|
||||
show() {
|
||||
return uni.$u.getPx(this.scrollTop) > uni.$u.getPx(this.top)
|
||||
},
|
||||
contentStyle() {
|
||||
const style = {}
|
||||
let radius = 0
|
||||
// 是否圆形
|
||||
if(this.mode === 'circle') {
|
||||
radius = '100px'
|
||||
} else {
|
||||
radius = '4px'
|
||||
}
|
||||
// 为了兼容安卓nvue,只能这么分开写
|
||||
style.borderTopLeftRadius = radius
|
||||
style.borderTopRightRadius = radius
|
||||
style.borderBottomLeftRadius = radius
|
||||
style.borderBottomRightRadius = radius
|
||||
return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
|
||||
}
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ["click"],
|
||||
// #endif
|
||||
methods: {
|
||||
backToTop() {
|
||||
// #ifdef APP-NVUE
|
||||
if (!this.$parent.$refs['u-back-top']) {
|
||||
uni.$u.error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`)
|
||||
}
|
||||
dom.scrollToElement(this.$parent.$refs['u-back-top'], {
|
||||
offset: 0
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-NVUE
|
||||
uni.pageScrollTo({
|
||||
scrollTop: 0,
|
||||
duration: this.duration
|
||||
});
|
||||
// #endif
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
$u-back-top-flex:1 !default;
|
||||
$u-back-top-height:100% !default;
|
||||
$u-back-top-background-color:#E1E1E1 !default;
|
||||
$u-back-top-tips-font-size:12px !default;
|
||||
.u-back-top {
|
||||
@include flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex:$u-back-top-flex;
|
||||
height: $u-back-top-height;
|
||||
justify-content: center;
|
||||
background-color: $u-back-top-background-color;
|
||||
|
||||
&__tips {
|
||||
font-size:$u-back-top-tips-font-size;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
export default {
|
||||
props: {
|
||||
// 是否显示圆点
|
||||
isDot: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.badge.isDot
|
||||
},
|
||||
// 显示的内容
|
||||
value: {
|
||||
type: [Number, String],
|
||||
default: ()=> uni.$u.props.badge.value
|
||||
},
|
||||
// #ifdef VUE3
|
||||
modelValue: {
|
||||
type: [Number, String],
|
||||
default: ()=> uni.$u.props.badge.value
|
||||
},
|
||||
// #endif
|
||||
// 是否显示
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.badge.show
|
||||
},
|
||||
// 最大值,超过最大值会显示 '{max}+'
|
||||
max: {
|
||||
type: [Number, String],
|
||||
default: ()=> uni.$u.props.badge.max
|
||||
},
|
||||
// 主题类型,error|warning|success|primary
|
||||
type: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.badge.type
|
||||
},
|
||||
// 当数值为 0 时,是否展示 Badge
|
||||
showZero: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.badge.showZero
|
||||
},
|
||||
// 背景颜色,优先级比type高,如设置,type参数会失效
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.badge.bgColor
|
||||
},
|
||||
// 字体颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.badge.color
|
||||
},
|
||||
// 徽标形状,circle-四角均为圆角,horn-左下角为直角
|
||||
shape: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.badge.shape
|
||||
},
|
||||
// 设置数字的显示方式,overflow|ellipsis|limit
|
||||
// overflow会根据max字段判断,超出显示`${max}+`
|
||||
// ellipsis会根据max判断,超出显示`${max}...`
|
||||
// limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
|
||||
numberType: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.badge.numberType
|
||||
},
|
||||
// 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
|
||||
offset: {
|
||||
type: Array,
|
||||
default: ()=> uni.$u.props.badge.offset
|
||||
},
|
||||
// 是否反转背景和字体颜色
|
||||
inverted: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.badge.inverted
|
||||
},
|
||||
// 是否绝对定位
|
||||
absolute: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.badge.absolute
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<text
|
||||
v-if="show && ((currentValue === 0 ? showZero : true) || isDot)"
|
||||
:class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]"
|
||||
:style="[$u.addStyle(customStyle), badgeStyle]"
|
||||
class="u-badge"
|
||||
>{{ isDot ? '' :showValue }}</text>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
/**
|
||||
* badge 徽标数
|
||||
* @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
|
||||
* @tutorial https://uviewui.com/components/badge.html
|
||||
*
|
||||
* @property {Boolean} isDot 是否显示圆点 (默认 false )
|
||||
* @property {String | Number} value 显示的内容
|
||||
* @property {Boolean} show 是否显示 (默认 true )
|
||||
* @property {String | Number} max 最大值,超过最大值会显示 '{max}+' (默认999)
|
||||
* @property {String} type 主题类型,error|warning|success|primary (默认 'error' )
|
||||
* @property {Boolean} showZero 当数值为 0 时,是否展示 Badge (默认 false )
|
||||
* @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效
|
||||
* @property {String} color 字体颜色 (默认 '#ffffff' )
|
||||
* @property {String} shape 徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' )
|
||||
* @property {String} numberType 设置数字的显示方式,overflow|ellipsis|limit (默认 'overflow' )
|
||||
* @property {Array}} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
|
||||
* @property {Boolean} inverted 是否反转背景和字体颜色(默认 false )
|
||||
* @property {Boolean} absolute 是否绝对定位(默认 false )
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
* @example <u-badge :type="type" :count="count"></u-badge>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-badge',
|
||||
mixins: [mpMixin, mixin, props],
|
||||
computed: {
|
||||
currentValue() {
|
||||
// #ifdef VUE2
|
||||
return Number(this.value);
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
return Number(this.modelValue || this.value);
|
||||
// #endif
|
||||
},
|
||||
// 是否将badge中心与父组件右上角重合
|
||||
boxStyle() {
|
||||
let style = {};
|
||||
return style;
|
||||
},
|
||||
// 整个组件的样式
|
||||
badgeStyle() {
|
||||
const style = {}
|
||||
if(this.color) {
|
||||
style.color = this.color
|
||||
}
|
||||
if (this.bgColor && !this.inverted) {
|
||||
style.backgroundColor = this.bgColor
|
||||
}
|
||||
if (this.absolute) {
|
||||
style.position = 'absolute'
|
||||
// 如果有设置offset参数
|
||||
if(this.offset.length) {
|
||||
// top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top
|
||||
const top = this.offset[0]
|
||||
const right = this.offset[1] || top
|
||||
style.top = uni.$u.addUnit(top)
|
||||
style.right = uni.$u.addUnit(right)
|
||||
}
|
||||
}
|
||||
return style
|
||||
},
|
||||
showValue() {
|
||||
switch (this.numberType) {
|
||||
case "overflow":
|
||||
return this.currentValue > Number(this.max) ? this.max + "+" : this.modelValue || this.value
|
||||
break;
|
||||
case "ellipsis":
|
||||
return this.currentValue > Number(this.max) ? "..." : this.modelValue || this.value
|
||||
break;
|
||||
case "limit":
|
||||
return this.currentValue > 999 ? this.currentValue >= 9999 ?
|
||||
Math.floor(this.modelValue || this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.modelValue || this.value /
|
||||
1e3 * 100) / 100 + "k" : this.modelValue || this.value
|
||||
break;
|
||||
default:
|
||||
return this.currentValue
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
$u-badge-primary: $u-primary !default;
|
||||
$u-badge-error: $u-error !default;
|
||||
$u-badge-success: $u-success !default;
|
||||
$u-badge-info: $u-info !default;
|
||||
$u-badge-warning: $u-warning !default;
|
||||
$u-badge-dot-radius: 100px !default;
|
||||
$u-badge-dot-size: 8px !default;
|
||||
$u-badge-dot-right: 4px !default;
|
||||
$u-badge-dot-top: 0 !default;
|
||||
$u-badge-text-font-size: 11px !default;
|
||||
$u-badge-text-right: 10px !default;
|
||||
$u-badge-text-padding: 2px 5px !default;
|
||||
$u-badge-text-align: center !default;
|
||||
$u-badge-text-color: #FFFFFF !default;
|
||||
|
||||
.u-badge {
|
||||
border-top-right-radius: $u-badge-dot-radius;
|
||||
border-top-left-radius: $u-badge-dot-radius;
|
||||
border-bottom-left-radius: $u-badge-dot-radius;
|
||||
border-bottom-right-radius: $u-badge-dot-radius;
|
||||
@include flex;
|
||||
line-height: $u-badge-text-font-size;
|
||||
text-align: $u-badge-text-align;
|
||||
font-size: $u-badge-text-font-size;
|
||||
color: $u-badge-text-color;
|
||||
|
||||
&--dot {
|
||||
height: $u-badge-dot-size;
|
||||
width: $u-badge-dot-size;
|
||||
}
|
||||
|
||||
&--inverted {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&--not-dot {
|
||||
padding: $u-badge-text-padding;
|
||||
}
|
||||
|
||||
&--horn {
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
background-color: $u-badge-primary;
|
||||
}
|
||||
|
||||
&--primary--inverted {
|
||||
color: $u-badge-primary;
|
||||
}
|
||||
|
||||
&--error {
|
||||
background-color: $u-badge-error;
|
||||
}
|
||||
|
||||
&--error--inverted {
|
||||
color: $u-badge-error;
|
||||
}
|
||||
|
||||
&--success {
|
||||
background-color: $u-badge-success;
|
||||
}
|
||||
|
||||
&--success--inverted {
|
||||
color: $u-badge-success;
|
||||
}
|
||||
|
||||
&--info {
|
||||
background-color: $u-badge-info;
|
||||
}
|
||||
|
||||
&--info--inverted {
|
||||
color: $u-badge-info;
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background-color: $u-badge-warning;
|
||||
}
|
||||
|
||||
&--warning--inverted {
|
||||
color: $u-badge-warning;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,208 @@
|
||||
// Import all the barcodes
|
||||
import barcodes from './barcodes/';
|
||||
|
||||
// Help functions
|
||||
import merge from './help/merge.js';
|
||||
import linearizeEncodings from './help/linearizeEncodings.js';
|
||||
import fixOptions from './help/fixOptions.js';
|
||||
import getRenderProperties from './help/getRenderProperties.js';
|
||||
import optionsFromStrings from './help/optionsFromStrings.js';
|
||||
|
||||
// Exceptions
|
||||
import ErrorHandler from './exceptions/ErrorHandler.js';
|
||||
import {InvalidInputException, NoElementException} from './exceptions/exceptions.js';
|
||||
|
||||
// Default values
|
||||
import defaults from './options/defaults.js';
|
||||
|
||||
// The protype of the object returned from the JsBarcode() call
|
||||
let API = function(){};
|
||||
|
||||
// The first call of the library API
|
||||
// Will return an object with all barcodes calls and the data that is used
|
||||
// by the renderers
|
||||
let JsBarcode = function(element, text, options){
|
||||
var api = new API();
|
||||
|
||||
if(typeof element === "undefined"){
|
||||
throw Error("No element to render on was provided.");
|
||||
}
|
||||
|
||||
// Variables that will be pased through the API calls
|
||||
api._renderProperties = getRenderProperties(element);
|
||||
api._encodings = [];
|
||||
api._options = defaults;
|
||||
api._errorHandler = new ErrorHandler(api);
|
||||
|
||||
// If text is set, use the simple syntax (render the barcode directly)
|
||||
if(typeof text !== "undefined"){
|
||||
options = options || {};
|
||||
|
||||
if(!options.format){
|
||||
options.format = autoSelectBarcode();
|
||||
}
|
||||
|
||||
api.options(options)[options.format](text, options).render();
|
||||
}
|
||||
|
||||
return api;
|
||||
};
|
||||
|
||||
// To make tests work TODO: remove
|
||||
JsBarcode.getModule = function(name){
|
||||
return barcodes[name];
|
||||
};
|
||||
|
||||
// Register all barcodes
|
||||
for(var name in barcodes){
|
||||
if(barcodes.hasOwnProperty(name)){ // Security check if the propery is a prototype property
|
||||
registerBarcode(barcodes, name);
|
||||
}
|
||||
}
|
||||
function registerBarcode(barcodes, name){
|
||||
API.prototype[name] =
|
||||
API.prototype[name.toUpperCase()] =
|
||||
API.prototype[name.toLowerCase()] =
|
||||
function(text, options){
|
||||
var api = this;
|
||||
return api._errorHandler.wrapBarcodeCall(function(){
|
||||
// Ensure text is options.text
|
||||
options.text = typeof options.text === 'undefined' ? undefined : '' + options.text;
|
||||
|
||||
var newOptions = merge(api._options, options);
|
||||
newOptions = optionsFromStrings(newOptions);
|
||||
var Encoder = barcodes[name];
|
||||
var encoded = encode(text, Encoder, newOptions);
|
||||
api._encodings.push(encoded);
|
||||
|
||||
return api;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// encode() handles the Encoder call and builds the binary string to be rendered
|
||||
function encode(text, Encoder, options){
|
||||
// Ensure that text is a string
|
||||
text = "" + text;
|
||||
|
||||
var encoder = new Encoder(text, options);
|
||||
|
||||
// If the input is not valid for the encoder, throw error.
|
||||
// If the valid callback option is set, call it instead of throwing error
|
||||
if(!encoder.valid()){
|
||||
throw new InvalidInputException(encoder.constructor.name, text);
|
||||
}
|
||||
|
||||
// Make a request for the binary data (and other infromation) that should be rendered
|
||||
var encoded = encoder.encode();
|
||||
|
||||
// Encodings can be nestled like [[1-1, 1-2], 2, [3-1, 3-2]
|
||||
// Convert to [1-1, 1-2, 2, 3-1, 3-2]
|
||||
encoded = linearizeEncodings(encoded);
|
||||
|
||||
// Merge
|
||||
for(let i = 0; i < encoded.length; i++){
|
||||
encoded[i].options = merge(options, encoded[i].options);
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
function autoSelectBarcode(){
|
||||
// If CODE128 exists. Use it
|
||||
if(barcodes["CODE128"]){
|
||||
return "CODE128";
|
||||
}
|
||||
|
||||
// Else, take the first (probably only) barcode
|
||||
return Object.keys(barcodes)[0];
|
||||
}
|
||||
|
||||
// Sets global encoder options
|
||||
// Added to the api by the JsBarcode function
|
||||
API.prototype.options = function(options){
|
||||
this._options = merge(this._options, options);
|
||||
return this;
|
||||
};
|
||||
|
||||
// Will create a blank space (usually in between barcodes)
|
||||
API.prototype.blank = function(size){
|
||||
const zeroes = new Array(size + 1).join("0");
|
||||
this._encodings.push({data: zeroes});
|
||||
return this;
|
||||
};
|
||||
|
||||
// Initialize JsBarcode on all HTML elements defined.
|
||||
API.prototype.init = function(){
|
||||
// Should do nothing if no elements where found
|
||||
if(!this._renderProperties){
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure renderProperies is an array
|
||||
if(!Array.isArray(this._renderProperties)){
|
||||
this._renderProperties = [this._renderProperties];
|
||||
}
|
||||
|
||||
var renderProperty;
|
||||
for(let i in this._renderProperties){
|
||||
renderProperty = this._renderProperties[i];
|
||||
var options = merge(this._options, renderProperty.options);
|
||||
|
||||
if(options.format == "auto"){
|
||||
options.format = autoSelectBarcode();
|
||||
}
|
||||
|
||||
this._errorHandler.wrapBarcodeCall(function(){
|
||||
var text = options.value;
|
||||
var Encoder = barcodes[options.format.toUpperCase()];
|
||||
var encoded = encode(text, Encoder, options);
|
||||
|
||||
render(renderProperty, encoded, options);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// The render API call. Calls the real render function.
|
||||
API.prototype.render = function(){
|
||||
if(!this._renderProperties){
|
||||
throw new NoElementException();
|
||||
}
|
||||
|
||||
if(Array.isArray(this._renderProperties)){
|
||||
for(var i = 0; i < this._renderProperties.length; i++){
|
||||
render(this._renderProperties[i], this._encodings, this._options);
|
||||
}
|
||||
}
|
||||
else{
|
||||
render(this._renderProperties, this._encodings, this._options);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
API.prototype._defaults = defaults;
|
||||
|
||||
// Prepares the encodings and calls the renderer
|
||||
function render(renderProperties, encodings, options){
|
||||
encodings = linearizeEncodings(encodings);
|
||||
|
||||
for(let i = 0; i < encodings.length; i++){
|
||||
encodings[i].options = merge(options, encodings[i].options);
|
||||
fixOptions(encodings[i].options);
|
||||
}
|
||||
|
||||
fixOptions(options);
|
||||
|
||||
var Renderer = renderProperties.renderer;
|
||||
var renderer = new Renderer(renderProperties.element, encodings, options);
|
||||
renderer.render();
|
||||
|
||||
if(renderProperties.afterRender){
|
||||
renderProperties.afterRender();
|
||||
}
|
||||
}
|
||||
|
||||
// Export to commonJS
|
||||
export default JsBarcode;
|
||||
@@ -0,0 +1,9 @@
|
||||
class Barcode{
|
||||
constructor(data, options){
|
||||
this.data = data;
|
||||
this.text = options.text || data;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
export default Barcode;
|
||||
@@ -0,0 +1,127 @@
|
||||
import Barcode from "../Barcode.js";
|
||||
import { SHIFT, SET_A, SET_B, MODULO, STOP, FNC1, SET_BY_CODE, SWAP, BARS } from './constants';
|
||||
|
||||
// This is the master class,
|
||||
// it does require the start code to be included in the string
|
||||
class CODE128 extends Barcode {
|
||||
constructor(data, options) {
|
||||
super(data.substring(1), options);
|
||||
|
||||
// Get array of ascii codes from data
|
||||
this.bytes = data.split('')
|
||||
.map(char => char.charCodeAt(0));
|
||||
}
|
||||
|
||||
valid() {
|
||||
// ASCII value ranges 0-127, 200-211
|
||||
return /^[\x00-\x7F\xC8-\xD3]+$/.test(this.data);
|
||||
}
|
||||
|
||||
// The public encoding function
|
||||
encode() {
|
||||
const bytes = this.bytes;
|
||||
// Remove the start code from the bytes and set its index
|
||||
const startIndex = bytes.shift() - 105;
|
||||
// Get start set by index
|
||||
const startSet = SET_BY_CODE[startIndex];
|
||||
|
||||
if (startSet === undefined) {
|
||||
throw new RangeError('The encoding does not start with a start character.');
|
||||
}
|
||||
|
||||
if (this.shouldEncodeAsEan128() === true) {
|
||||
bytes.unshift(FNC1);
|
||||
}
|
||||
|
||||
// Start encode with the right type
|
||||
const encodingResult = CODE128.next(bytes, 1, startSet);
|
||||
|
||||
return {
|
||||
text:
|
||||
this.text === this.data
|
||||
? this.text.replace(/[^\x20-\x7E]/g, '')
|
||||
: this.text,
|
||||
data:
|
||||
// Add the start bits
|
||||
CODE128.getBar(startIndex) +
|
||||
// Add the encoded bits
|
||||
encodingResult.result +
|
||||
// Add the checksum
|
||||
CODE128.getBar((encodingResult.checksum + startIndex) % MODULO) +
|
||||
// Add the end bits
|
||||
CODE128.getBar(STOP)
|
||||
};
|
||||
}
|
||||
|
||||
// GS1-128/EAN-128
|
||||
shouldEncodeAsEan128() {
|
||||
let isEAN128 = this.options.ean128 || false;
|
||||
if (typeof isEAN128 === 'string') {
|
||||
isEAN128 = isEAN128.toLowerCase() === 'true';
|
||||
}
|
||||
return isEAN128;
|
||||
}
|
||||
|
||||
// Get a bar symbol by index
|
||||
static getBar(index) {
|
||||
return BARS[index] ? BARS[index].toString() : '';
|
||||
}
|
||||
|
||||
// Correct an index by a set and shift it from the bytes array
|
||||
static correctIndex(bytes, set) {
|
||||
if (set === SET_A) {
|
||||
const charCode = bytes.shift();
|
||||
return charCode < 32 ? charCode + 64 : charCode - 32;
|
||||
} else if (set === SET_B) {
|
||||
return bytes.shift() - 32;
|
||||
} else {
|
||||
return (bytes.shift() - 48) * 10 + bytes.shift() - 48;
|
||||
}
|
||||
}
|
||||
|
||||
static next(bytes, pos, set) {
|
||||
if (!bytes.length) {
|
||||
return { result: '', checksum: 0 };
|
||||
}
|
||||
|
||||
let nextCode, index;
|
||||
|
||||
// Special characters
|
||||
if (bytes[0] >= 200){
|
||||
index = bytes.shift() - 105;
|
||||
const nextSet = SWAP[index];
|
||||
|
||||
// Swap to other set
|
||||
if (nextSet !== undefined) {
|
||||
nextCode = CODE128.next(bytes, pos + 1, nextSet);
|
||||
}
|
||||
// Continue on current set but encode a special character
|
||||
else {
|
||||
// Shift
|
||||
if ((set === SET_A || set === SET_B) && index === SHIFT) {
|
||||
// Convert the next character so that is encoded correctly
|
||||
bytes[0] = (set === SET_A)
|
||||
? bytes[0] > 95 ? bytes[0] - 96 : bytes[0]
|
||||
: bytes[0] < 32 ? bytes[0] + 96 : bytes[0];
|
||||
}
|
||||
nextCode = CODE128.next(bytes, pos + 1, set);
|
||||
}
|
||||
}
|
||||
// Continue encoding
|
||||
else {
|
||||
index = CODE128.correctIndex(bytes, set);
|
||||
nextCode = CODE128.next(bytes, pos + 1, set);
|
||||
}
|
||||
|
||||
// Get the correct binary encoding and calculate the weight
|
||||
const enc = CODE128.getBar(index);
|
||||
const weight = index * pos;
|
||||
|
||||
return {
|
||||
result: enc + nextCode.result,
|
||||
checksum: weight + nextCode.checksum
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default CODE128;
|
||||
@@ -0,0 +1,14 @@
|
||||
import CODE128 from './CODE128.js';
|
||||
import { A_START_CHAR, A_CHARS } from './constants';
|
||||
|
||||
class CODE128A extends CODE128 {
|
||||
constructor(string, options) {
|
||||
super(A_START_CHAR + string, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return (new RegExp(`^${A_CHARS}+$`)).test(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
export default CODE128A;
|
||||
@@ -0,0 +1,14 @@
|
||||
import CODE128 from './CODE128.js';
|
||||
import { B_START_CHAR, B_CHARS } from './constants';
|
||||
|
||||
class CODE128B extends CODE128 {
|
||||
constructor(string, options) {
|
||||
super(B_START_CHAR + string, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return (new RegExp(`^${B_CHARS}+$`)).test(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
export default CODE128B;
|
||||
@@ -0,0 +1,14 @@
|
||||
import CODE128 from './CODE128.js';
|
||||
import { C_START_CHAR, C_CHARS } from './constants';
|
||||
|
||||
class CODE128C extends CODE128 {
|
||||
constructor(string, options) {
|
||||
super(C_START_CHAR + string, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return (new RegExp(`^${C_CHARS}+$`)).test(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
export default CODE128C;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import CODE128 from './CODE128';
|
||||
import autoSelectModes from './auto';
|
||||
|
||||
class CODE128AUTO extends CODE128{
|
||||
constructor(data, options){
|
||||
// ASCII value ranges 0-127, 200-211
|
||||
if (/^[\x00-\x7F\xC8-\xD3]+$/.test(data)) {
|
||||
super(autoSelectModes(data), options);
|
||||
} else{
|
||||
super(data, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default CODE128AUTO;
|
||||
@@ -0,0 +1,68 @@
|
||||
import { A_START_CHAR, B_START_CHAR, C_START_CHAR, A_CHARS, B_CHARS, C_CHARS } from './constants';
|
||||
|
||||
// Match Set functions
|
||||
const matchSetALength = (string) => string.match(new RegExp(`^${A_CHARS}*`))[0].length;
|
||||
const matchSetBLength = (string) => string.match(new RegExp(`^${B_CHARS}*`))[0].length;
|
||||
const matchSetC = (string) => string.match(new RegExp(`^${C_CHARS}*`))[0];
|
||||
|
||||
// CODE128A or CODE128B
|
||||
function autoSelectFromAB(string, isA){
|
||||
const ranges = isA ? A_CHARS : B_CHARS;
|
||||
const untilC = string.match(new RegExp(`^(${ranges}+?)(([0-9]{2}){2,})([^0-9]|$)`));
|
||||
|
||||
if (untilC) {
|
||||
return (
|
||||
untilC[1] +
|
||||
String.fromCharCode(204) +
|
||||
autoSelectFromC(string.substring(untilC[1].length))
|
||||
);
|
||||
}
|
||||
|
||||
const chars = string.match(new RegExp(`^${ranges}+`))[0];
|
||||
|
||||
if (chars.length === string.length) {
|
||||
return string;
|
||||
}
|
||||
|
||||
return (
|
||||
chars +
|
||||
String.fromCharCode(isA ? 205 : 206) +
|
||||
autoSelectFromAB(string.substring(chars.length), !isA)
|
||||
);
|
||||
}
|
||||
|
||||
// CODE128C
|
||||
function autoSelectFromC(string) {
|
||||
const cMatch = matchSetC(string);
|
||||
const length = cMatch.length;
|
||||
|
||||
if (length === string.length) {
|
||||
return string;
|
||||
}
|
||||
|
||||
string = string.substring(length);
|
||||
|
||||
// Select A/B depending on the longest match
|
||||
const isA = matchSetALength(string) >= matchSetBLength(string);
|
||||
return cMatch + String.fromCharCode(isA ? 206 : 205) + autoSelectFromAB(string, isA);
|
||||
}
|
||||
|
||||
// Detect Code Set (A, B or C) and format the string
|
||||
export default (string) => {
|
||||
let newString;
|
||||
const cLength = matchSetC(string).length;
|
||||
|
||||
// Select 128C if the string start with enough digits
|
||||
if (cLength >= 2) {
|
||||
newString = C_START_CHAR + autoSelectFromC(string);
|
||||
} else {
|
||||
// Select A/B depending on the longest match
|
||||
const isA = matchSetALength(string) > matchSetBLength(string);
|
||||
newString = (isA ? A_START_CHAR : B_START_CHAR) + autoSelectFromAB(string, isA);
|
||||
}
|
||||
|
||||
return newString.replace(
|
||||
/[\xCD\xCE]([^])[\xCD\xCE]/, // Any sequence between 205 and 206 characters
|
||||
(match, char) => String.fromCharCode(203) + char
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
// constants for internal usage
|
||||
export const SET_A = 0;
|
||||
export const SET_B = 1;
|
||||
export const SET_C = 2;
|
||||
|
||||
// Special characters
|
||||
export const SHIFT = 98;
|
||||
export const START_A = 103;
|
||||
export const START_B = 104;
|
||||
export const START_C = 105;
|
||||
export const MODULO = 103;
|
||||
export const STOP = 106;
|
||||
export const FNC1 = 207;
|
||||
|
||||
// Get set by start code
|
||||
export const SET_BY_CODE = {
|
||||
[START_A]: SET_A,
|
||||
[START_B]: SET_B,
|
||||
[START_C]: SET_C,
|
||||
};
|
||||
|
||||
// Get next set by code
|
||||
export const SWAP = {
|
||||
101: SET_A,
|
||||
100: SET_B,
|
||||
99: SET_C,
|
||||
};
|
||||
|
||||
export const A_START_CHAR = String.fromCharCode(208); // START_A + 105
|
||||
export const B_START_CHAR = String.fromCharCode(209); // START_B + 105
|
||||
export const C_START_CHAR = String.fromCharCode(210); // START_C + 105
|
||||
|
||||
// 128A (Code Set A)
|
||||
// ASCII characters 00 to 95 (0–9, A–Z and control codes), special characters, and FNC 1–4
|
||||
export const A_CHARS = "[\x00-\x5F\xC8-\xCF]";
|
||||
|
||||
// 128B (Code Set B)
|
||||
// ASCII characters 32 to 127 (0–9, A–Z, a–z), special characters, and FNC 1–4
|
||||
export const B_CHARS = "[\x20-\x7F\xC8-\xCF]";
|
||||
|
||||
// 128C (Code Set C)
|
||||
// 00–99 (encodes two digits with a single code point) and FNC1
|
||||
export const C_CHARS = "(\xCF*[0-9]{2}\xCF*)";
|
||||
|
||||
// CODE128 includes 107 symbols:
|
||||
// 103 data symbols, 3 start symbols (A, B and C), and 1 stop symbol (the last one)
|
||||
// Each symbol consist of three black bars (1) and three white spaces (0).
|
||||
export const BARS = [
|
||||
11011001100, 11001101100, 11001100110, 10010011000, 10010001100,
|
||||
10001001100, 10011001000, 10011000100, 10001100100, 11001001000,
|
||||
11001000100, 11000100100, 10110011100, 10011011100, 10011001110,
|
||||
10111001100, 10011101100, 10011100110, 11001110010, 11001011100,
|
||||
11001001110, 11011100100, 11001110100, 11101101110, 11101001100,
|
||||
11100101100, 11100100110, 11101100100, 11100110100, 11100110010,
|
||||
11011011000, 11011000110, 11000110110, 10100011000, 10001011000,
|
||||
10001000110, 10110001000, 10001101000, 10001100010, 11010001000,
|
||||
11000101000, 11000100010, 10110111000, 10110001110, 10001101110,
|
||||
10111011000, 10111000110, 10001110110, 11101110110, 11010001110,
|
||||
11000101110, 11011101000, 11011100010, 11011101110, 11101011000,
|
||||
11101000110, 11100010110, 11101101000, 11101100010, 11100011010,
|
||||
11101111010, 11001000010, 11110001010, 10100110000, 10100001100,
|
||||
10010110000, 10010000110, 10000101100, 10000100110, 10110010000,
|
||||
10110000100, 10011010000, 10011000010, 10000110100, 10000110010,
|
||||
11000010010, 11001010000, 11110111010, 11000010100, 10001111010,
|
||||
10100111100, 10010111100, 10010011110, 10111100100, 10011110100,
|
||||
10011110010, 11110100100, 11110010100, 11110010010, 11011011110,
|
||||
11011110110, 11110110110, 10101111000, 10100011110, 10001011110,
|
||||
10111101000, 10111100010, 11110101000, 11110100010, 10111011110,
|
||||
10111101110, 11101011110, 11110101110, 11010000100, 11010010000,
|
||||
11010011100, 1100011101011
|
||||
];
|
||||
@@ -0,0 +1,6 @@
|
||||
import CODE128 from './CODE128_AUTO.js';
|
||||
import CODE128A from './CODE128A.js';
|
||||
import CODE128B from './CODE128B.js';
|
||||
import CODE128C from './CODE128C.js';
|
||||
|
||||
export {CODE128, CODE128A, CODE128B, CODE128C};
|
||||
@@ -0,0 +1,105 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/Code_39#Encoding
|
||||
|
||||
import Barcode from "../Barcode.js";
|
||||
|
||||
class CODE39 extends Barcode {
|
||||
constructor(data, options){
|
||||
data = data.toUpperCase();
|
||||
|
||||
// Calculate mod43 checksum if enabled
|
||||
if(options.mod43){
|
||||
data += getCharacter(mod43checksum(data));
|
||||
}
|
||||
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
encode(){
|
||||
// First character is always a *
|
||||
var result = getEncoding("*");
|
||||
|
||||
// Take every character and add the binary representation to the result
|
||||
for(let i = 0; i < this.data.length; i++){
|
||||
result += getEncoding(this.data[i]) + "0";
|
||||
}
|
||||
|
||||
// Last character is always a *
|
||||
result += getEncoding("*");
|
||||
|
||||
return {
|
||||
data: result,
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
valid(){
|
||||
return this.data.search(/^[0-9A-Z\-\.\ \$\/\+\%]+$/) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// All characters. The position in the array is the (checksum) value
|
||||
var characters = [
|
||||
"0", "1", "2", "3",
|
||||
"4", "5", "6", "7",
|
||||
"8", "9", "A", "B",
|
||||
"C", "D", "E", "F",
|
||||
"G", "H", "I", "J",
|
||||
"K", "L", "M", "N",
|
||||
"O", "P", "Q", "R",
|
||||
"S", "T", "U", "V",
|
||||
"W", "X", "Y", "Z",
|
||||
"-", ".", " ", "$",
|
||||
"/", "+", "%", "*"
|
||||
];
|
||||
|
||||
// The decimal representation of the characters, is converted to the
|
||||
// corresponding binary with the getEncoding function
|
||||
var encodings = [
|
||||
20957, 29783, 23639, 30485,
|
||||
20951, 29813, 23669, 20855,
|
||||
29789, 23645, 29975, 23831,
|
||||
30533, 22295, 30149, 24005,
|
||||
21623, 29981, 23837, 22301,
|
||||
30023, 23879, 30545, 22343,
|
||||
30161, 24017, 21959, 30065,
|
||||
23921, 22385, 29015, 18263,
|
||||
29141, 17879, 29045, 18293,
|
||||
17783, 29021, 18269, 17477,
|
||||
17489, 17681, 20753, 35770
|
||||
];
|
||||
|
||||
// Get the binary representation of a character by converting the encodings
|
||||
// from decimal to binary
|
||||
function getEncoding(character){
|
||||
return getBinary(characterValue(character));
|
||||
}
|
||||
|
||||
function getBinary(characterValue){
|
||||
return encodings[characterValue].toString(2);
|
||||
}
|
||||
|
||||
function getCharacter(characterValue){
|
||||
return characters[characterValue];
|
||||
}
|
||||
|
||||
function characterValue(character){
|
||||
return characters.indexOf(character);
|
||||
}
|
||||
|
||||
function mod43checksum(data){
|
||||
var checksum = 0;
|
||||
for(let i = 0; i < data.length; i++){
|
||||
checksum += characterValue(data[i]);
|
||||
}
|
||||
|
||||
checksum = checksum % 43;
|
||||
return checksum;
|
||||
}
|
||||
|
||||
export {CODE39};
|
||||
@@ -0,0 +1,73 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/Code_93#Detailed_outline
|
||||
|
||||
import { SYMBOLS, BINARIES, MULTI_SYMBOLS } from './constants';
|
||||
import Barcode from "../Barcode.js";
|
||||
|
||||
class CODE93 extends Barcode {
|
||||
constructor(data, options){
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid(){
|
||||
return /^[0-9A-Z\-. $/+%]+$/.test(this.data);
|
||||
}
|
||||
|
||||
encode(){
|
||||
const symbols = this.data
|
||||
.split('')
|
||||
.flatMap(c => MULTI_SYMBOLS[c] || c);
|
||||
const encoded = symbols
|
||||
.map(s => CODE93.getEncoding(s))
|
||||
.join('');
|
||||
|
||||
// Compute checksum symbols
|
||||
const csumC = CODE93.checksum(symbols, 20);
|
||||
const csumK = CODE93.checksum(symbols.concat(csumC), 15);
|
||||
|
||||
return {
|
||||
text: this.text,
|
||||
data:
|
||||
// Add the start bits
|
||||
CODE93.getEncoding('\xff') +
|
||||
// Add the encoded bits
|
||||
encoded +
|
||||
// Add the checksum
|
||||
CODE93.getEncoding(csumC) + CODE93.getEncoding(csumK) +
|
||||
// Add the stop bits
|
||||
CODE93.getEncoding('\xff') +
|
||||
// Add the termination bit
|
||||
'1'
|
||||
};
|
||||
}
|
||||
|
||||
// Get the binary encoding of a symbol
|
||||
static getEncoding(symbol) {
|
||||
return BINARIES[CODE93.symbolValue(symbol)];
|
||||
}
|
||||
|
||||
// Get the symbol for a symbol value
|
||||
static getSymbol(symbolValue) {
|
||||
return SYMBOLS[symbolValue];
|
||||
}
|
||||
|
||||
// Get the symbol value of a symbol
|
||||
static symbolValue(symbol) {
|
||||
return SYMBOLS.indexOf(symbol);
|
||||
}
|
||||
|
||||
// Calculate a checksum symbol
|
||||
static checksum(symbols, maxWeight) {
|
||||
const csum = symbols
|
||||
.slice()
|
||||
.reverse()
|
||||
.reduce((sum, symbol, idx) => {
|
||||
const weight = (idx % maxWeight) + 1;
|
||||
return sum + (CODE93.symbolValue(symbol) * weight);
|
||||
}, 0);
|
||||
|
||||
return CODE93.getSymbol(csum % 47);
|
||||
}
|
||||
}
|
||||
|
||||
export default CODE93;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Encoding documentation
|
||||
// https://en.wikipedia.org/wiki/Code_93#Full_ASCII_Code_93
|
||||
|
||||
import CODE93 from './CODE93.js';
|
||||
|
||||
class CODE93FullASCII extends CODE93 {
|
||||
constructor(data, options) {
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return /^[\x00-\x7f]+$/.test(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
export default CODE93FullASCII;
|
||||
@@ -0,0 +1,123 @@
|
||||
// The position in the array is the (checksum) value
|
||||
export const SYMBOLS = [
|
||||
'0', '1', '2', '3',
|
||||
'4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'I', 'J',
|
||||
'K', 'L', 'M', 'N',
|
||||
'O', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V',
|
||||
'W', 'X', 'Y', 'Z',
|
||||
'-', '.', ' ', '$',
|
||||
'/', '+', '%',
|
||||
// Only used for csum and multi-symbols character encodings
|
||||
'($)', '(%)', '(/)', '(+)',
|
||||
// Start/Stop
|
||||
'\xff',
|
||||
];
|
||||
|
||||
// Order matches SYMBOLS array
|
||||
export const BINARIES = [
|
||||
'100010100', '101001000', '101000100', '101000010',
|
||||
'100101000', '100100100', '100100010', '101010000',
|
||||
'100010010', '100001010', '110101000', '110100100',
|
||||
'110100010', '110010100', '110010010', '110001010',
|
||||
'101101000', '101100100', '101100010', '100110100',
|
||||
'100011010', '101011000', '101001100', '101000110',
|
||||
'100101100', '100010110', '110110100', '110110010',
|
||||
'110101100', '110100110', '110010110', '110011010',
|
||||
'101101100', '101100110', '100110110', '100111010',
|
||||
'100101110', '111010100', '111010010', '111001010',
|
||||
'101101110', '101110110', '110101110', '100100110',
|
||||
'111011010', '111010110', '100110010', '101011110',
|
||||
];
|
||||
|
||||
// Multi-symbol characters (Full ASCII Code 93)
|
||||
export const MULTI_SYMBOLS = {
|
||||
'\x00': ['(%)', 'U'],
|
||||
'\x01': ['($)', 'A'],
|
||||
'\x02': ['($)', 'B'],
|
||||
'\x03': ['($)', 'C'],
|
||||
'\x04': ['($)', 'D'],
|
||||
'\x05': ['($)', 'E'],
|
||||
'\x06': ['($)', 'F'],
|
||||
'\x07': ['($)', 'G'],
|
||||
'\x08': ['($)', 'H'],
|
||||
'\x09': ['($)', 'I'],
|
||||
'\x0a': ['($)', 'J'],
|
||||
'\x0b': ['($)', 'K'],
|
||||
'\x0c': ['($)', 'L'],
|
||||
'\x0d': ['($)', 'M'],
|
||||
'\x0e': ['($)', 'N'],
|
||||
'\x0f': ['($)', 'O'],
|
||||
'\x10': ['($)', 'P'],
|
||||
'\x11': ['($)', 'Q'],
|
||||
'\x12': ['($)', 'R'],
|
||||
'\x13': ['($)', 'S'],
|
||||
'\x14': ['($)', 'T'],
|
||||
'\x15': ['($)', 'U'],
|
||||
'\x16': ['($)', 'V'],
|
||||
'\x17': ['($)', 'W'],
|
||||
'\x18': ['($)', 'X'],
|
||||
'\x19': ['($)', 'Y'],
|
||||
'\x1a': ['($)', 'Z'],
|
||||
'\x1b': ['(%)', 'A'],
|
||||
'\x1c': ['(%)', 'B'],
|
||||
'\x1d': ['(%)', 'C'],
|
||||
'\x1e': ['(%)', 'D'],
|
||||
'\x1f': ['(%)', 'E'],
|
||||
'!': ['(/)', 'A'],
|
||||
'"': ['(/)', 'B'],
|
||||
'#': ['(/)', 'C'],
|
||||
'&': ['(/)', 'F'],
|
||||
'\'': ['(/)', 'G'],
|
||||
'(': ['(/)', 'H'],
|
||||
')': ['(/)', 'I'],
|
||||
'*': ['(/)', 'J'],
|
||||
',': ['(/)', 'L'],
|
||||
':': ['(/)', 'Z'],
|
||||
';': ['(%)', 'F'],
|
||||
'<': ['(%)', 'G'],
|
||||
'=': ['(%)', 'H'],
|
||||
'>': ['(%)', 'I'],
|
||||
'?': ['(%)', 'J'],
|
||||
'@': ['(%)', 'V'],
|
||||
'[': ['(%)', 'K'],
|
||||
'\\': ['(%)', 'L'],
|
||||
']': ['(%)', 'M'],
|
||||
'^': ['(%)', 'N'],
|
||||
'_': ['(%)', 'O'],
|
||||
'`': ['(%)', 'W'],
|
||||
'a': ['(+)', 'A'],
|
||||
'b': ['(+)', 'B'],
|
||||
'c': ['(+)', 'C'],
|
||||
'd': ['(+)', 'D'],
|
||||
'e': ['(+)', 'E'],
|
||||
'f': ['(+)', 'F'],
|
||||
'g': ['(+)', 'G'],
|
||||
'h': ['(+)', 'H'],
|
||||
'i': ['(+)', 'I'],
|
||||
'j': ['(+)', 'J'],
|
||||
'k': ['(+)', 'K'],
|
||||
'l': ['(+)', 'L'],
|
||||
'm': ['(+)', 'M'],
|
||||
'n': ['(+)', 'N'],
|
||||
'o': ['(+)', 'O'],
|
||||
'p': ['(+)', 'P'],
|
||||
'q': ['(+)', 'Q'],
|
||||
'r': ['(+)', 'R'],
|
||||
's': ['(+)', 'S'],
|
||||
't': ['(+)', 'T'],
|
||||
'u': ['(+)', 'U'],
|
||||
'v': ['(+)', 'V'],
|
||||
'w': ['(+)', 'W'],
|
||||
'x': ['(+)', 'X'],
|
||||
'y': ['(+)', 'Y'],
|
||||
'z': ['(+)', 'Z'],
|
||||
'{': ['(%)', 'P'],
|
||||
'|': ['(%)', 'Q'],
|
||||
'}': ['(%)', 'R'],
|
||||
'~': ['(%)', 'S'],
|
||||
'\x7f': ['(%)', 'T'],
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
import CODE93 from './CODE93.js';
|
||||
import CODE93FullASCII from './CODE93FullASCII.js';
|
||||
|
||||
export {CODE93, CODE93FullASCII};
|
||||
@@ -0,0 +1,72 @@
|
||||
import { SIDE_BIN, MIDDLE_BIN } from './constants';
|
||||
import encode from './encoder';
|
||||
import Barcode from '../Barcode';
|
||||
|
||||
// Base class for EAN8 & EAN13
|
||||
class EAN extends Barcode {
|
||||
|
||||
constructor(data, options) {
|
||||
super(data, options);
|
||||
|
||||
// Make sure the font is not bigger than the space between the guard bars
|
||||
this.fontSize = !options.flat && options.fontSize > options.width * 10
|
||||
? options.width * 10
|
||||
: options.fontSize;
|
||||
|
||||
// Make the guard bars go down half the way of the text
|
||||
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||
}
|
||||
|
||||
encode() {
|
||||
return this.options.flat
|
||||
? this.encodeFlat()
|
||||
: this.encodeGuarded();
|
||||
}
|
||||
|
||||
leftText(from, to) {
|
||||
return this.text.substr(from, to);
|
||||
}
|
||||
|
||||
leftEncode(data, structure) {
|
||||
return encode(data, structure);
|
||||
}
|
||||
|
||||
rightText(from, to) {
|
||||
return this.text.substr(from, to);
|
||||
}
|
||||
|
||||
rightEncode(data, structure) {
|
||||
return encode(data, structure);
|
||||
}
|
||||
|
||||
encodeGuarded() {
|
||||
const textOptions = { fontSize: this.fontSize };
|
||||
const guardOptions = { height: this.guardHeight };
|
||||
|
||||
return [
|
||||
{ data: SIDE_BIN, options: guardOptions },
|
||||
{ data: this.leftEncode(), text: this.leftText(), options: textOptions },
|
||||
{ data: MIDDLE_BIN, options: guardOptions },
|
||||
{ data: this.rightEncode(), text: this.rightText(), options: textOptions },
|
||||
{ data: SIDE_BIN, options: guardOptions },
|
||||
];
|
||||
}
|
||||
|
||||
encodeFlat() {
|
||||
const data = [
|
||||
SIDE_BIN,
|
||||
this.leftEncode(),
|
||||
MIDDLE_BIN,
|
||||
this.rightEncode(),
|
||||
SIDE_BIN
|
||||
];
|
||||
|
||||
return {
|
||||
data: data.join(''),
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN;
|
||||
@@ -0,0 +1,90 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Binary_encoding_of_data_digits_into_EAN-13_barcode
|
||||
|
||||
import { EAN13_STRUCTURE } from './constants';
|
||||
import EAN from './EAN';
|
||||
|
||||
// Calculate the checksum digit
|
||||
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Calculation_of_checksum_digit
|
||||
const checksum = (number) => {
|
||||
const res = number
|
||||
.substr(0, 12)
|
||||
.split('')
|
||||
.map((n) => +n)
|
||||
.reduce((sum, a, idx) => (
|
||||
idx % 2 ? sum + a * 3 : sum + a
|
||||
), 0);
|
||||
|
||||
return (10 - (res % 10)) % 10;
|
||||
};
|
||||
|
||||
class EAN13 extends EAN {
|
||||
|
||||
constructor(data, options) {
|
||||
// Add checksum if it does not exist
|
||||
if (data.search(/^[0-9]{12}$/) !== -1) {
|
||||
data += checksum(data);
|
||||
}
|
||||
|
||||
super(data, options);
|
||||
|
||||
// Adds a last character to the end of the barcode
|
||||
this.lastChar = options.lastChar;
|
||||
}
|
||||
|
||||
valid() {
|
||||
return (
|
||||
this.data.search(/^[0-9]{13}$/) !== -1 &&
|
||||
+this.data[12] === checksum(this.data)
|
||||
);
|
||||
}
|
||||
|
||||
leftText() {
|
||||
return super.leftText(1, 6);
|
||||
}
|
||||
|
||||
leftEncode() {
|
||||
const data = this.data.substr(1, 6);
|
||||
const structure = EAN13_STRUCTURE[this.data[0]];
|
||||
return super.leftEncode(data, structure);
|
||||
}
|
||||
|
||||
rightText() {
|
||||
return super.rightText(7, 6);
|
||||
}
|
||||
|
||||
rightEncode() {
|
||||
const data = this.data.substr(7, 6);
|
||||
return super.rightEncode(data, 'RRRRRR');
|
||||
}
|
||||
|
||||
// The "standard" way of printing EAN13 barcodes with guard bars
|
||||
encodeGuarded() {
|
||||
const data = super.encodeGuarded();
|
||||
|
||||
// Extend data with left digit & last character
|
||||
if (this.options.displayValue) {
|
||||
data.unshift({
|
||||
data: '000000000000',
|
||||
text: this.text.substr(0, 1),
|
||||
options: { textAlign: 'left', fontSize: this.fontSize }
|
||||
});
|
||||
|
||||
if (this.options.lastChar) {
|
||||
data.push({
|
||||
data: '00'
|
||||
});
|
||||
data.push({
|
||||
data: '00000',
|
||||
text: this.options.lastChar,
|
||||
options: { fontSize: this.fontSize }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN13;
|
||||
@@ -0,0 +1,30 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/EAN_2#Encoding
|
||||
|
||||
import { EAN2_STRUCTURE } from './constants';
|
||||
import encode from './encoder';
|
||||
import Barcode from '../Barcode';
|
||||
|
||||
class EAN2 extends Barcode {
|
||||
|
||||
constructor(data, options) {
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return this.data.search(/^[0-9]{2}$/) !== -1;
|
||||
}
|
||||
|
||||
encode(){
|
||||
// Choose the structure based on the number mod 4
|
||||
const structure = EAN2_STRUCTURE[parseInt(this.data) % 4];
|
||||
return {
|
||||
// Start bits + Encode the two digits with 01 in between
|
||||
data: '1011' + encode(this.data, structure, '01'),
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN2;
|
||||
@@ -0,0 +1,40 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/EAN_5#Encoding
|
||||
|
||||
import { EAN5_STRUCTURE } from './constants';
|
||||
import encode from './encoder';
|
||||
import Barcode from '../Barcode';
|
||||
|
||||
const checksum = (data) => {
|
||||
const result = data
|
||||
.split('')
|
||||
.map(n => +n)
|
||||
.reduce((sum, a, idx) => {
|
||||
return idx % 2
|
||||
? sum + a * 9
|
||||
: sum + a * 3;
|
||||
}, 0);
|
||||
return result % 10;
|
||||
};
|
||||
|
||||
class EAN5 extends Barcode {
|
||||
|
||||
constructor(data, options) {
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return this.data.search(/^[0-9]{5}$/) !== -1;
|
||||
}
|
||||
|
||||
encode() {
|
||||
const structure = EAN5_STRUCTURE[checksum(this.data)];
|
||||
return {
|
||||
data: '1011' + encode(this.data, structure, '01'),
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN5;
|
||||
@@ -0,0 +1,57 @@
|
||||
// Encoding documentation:
|
||||
// http://www.barcodeisland.com/ean8.phtml
|
||||
|
||||
import EAN from './EAN';
|
||||
|
||||
// Calculate the checksum digit
|
||||
const checksum = (number) => {
|
||||
const res = number
|
||||
.substr(0, 7)
|
||||
.split('')
|
||||
.map((n) => +n)
|
||||
.reduce((sum, a, idx) => (
|
||||
idx % 2 ? sum + a : sum + a * 3
|
||||
), 0);
|
||||
|
||||
return (10 - (res % 10)) % 10;
|
||||
};
|
||||
|
||||
class EAN8 extends EAN {
|
||||
|
||||
constructor(data, options) {
|
||||
// Add checksum if it does not exist
|
||||
if (data.search(/^[0-9]{7}$/) !== -1) {
|
||||
data += checksum(data);
|
||||
}
|
||||
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return (
|
||||
this.data.search(/^[0-9]{8}$/) !== -1 &&
|
||||
+this.data[7] === checksum(this.data)
|
||||
);
|
||||
}
|
||||
|
||||
leftText() {
|
||||
return super.leftText(0, 4);
|
||||
}
|
||||
|
||||
leftEncode() {
|
||||
const data = this.data.substr(0, 4);
|
||||
return super.leftEncode(data, 'LLLL');
|
||||
}
|
||||
|
||||
rightText() {
|
||||
return super.rightText(4, 4);
|
||||
}
|
||||
|
||||
rightEncode() {
|
||||
const data = this.data.substr(4, 4);
|
||||
return super.rightEncode(data, 'RRRR');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN8;
|
||||
@@ -0,0 +1,132 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/Universal_Product_Code#Encoding
|
||||
|
||||
import encode from './encoder';
|
||||
import Barcode from "../Barcode.js";
|
||||
|
||||
class UPC extends Barcode{
|
||||
constructor(data, options){
|
||||
// Add checksum if it does not exist
|
||||
if(data.search(/^[0-9]{11}$/) !== -1){
|
||||
data += checksum(data);
|
||||
}
|
||||
|
||||
super(data, options);
|
||||
|
||||
this.displayValue = options.displayValue;
|
||||
|
||||
// Make sure the font is not bigger than the space between the guard bars
|
||||
if(options.fontSize > options.width * 10){
|
||||
this.fontSize = options.width * 10;
|
||||
}
|
||||
else{
|
||||
this.fontSize = options.fontSize;
|
||||
}
|
||||
|
||||
// Make the guard bars go down half the way of the text
|
||||
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||
}
|
||||
|
||||
valid(){
|
||||
return this.data.search(/^[0-9]{12}$/) !== -1 &&
|
||||
this.data[11] == checksum(this.data);
|
||||
}
|
||||
|
||||
encode(){
|
||||
if(this.options.flat){
|
||||
return this.flatEncoding();
|
||||
}
|
||||
else{
|
||||
return this.guardedEncoding();
|
||||
}
|
||||
}
|
||||
|
||||
flatEncoding(){
|
||||
var result = "";
|
||||
|
||||
result += "101";
|
||||
result += encode(this.data.substr(0, 6), "LLLLLL");
|
||||
result += "01010";
|
||||
result += encode(this.data.substr(6, 6), "RRRRRR");
|
||||
result += "101";
|
||||
|
||||
return {
|
||||
data: result,
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
guardedEncoding(){
|
||||
var result = [];
|
||||
|
||||
// Add the first digit
|
||||
if(this.displayValue){
|
||||
result.push({
|
||||
data: "00000000",
|
||||
text: this.text.substr(0, 1),
|
||||
options: {textAlign: "left", fontSize: this.fontSize}
|
||||
});
|
||||
}
|
||||
|
||||
// Add the guard bars
|
||||
result.push({
|
||||
data: "101" + encode(this.data[0], "L"),
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the left side
|
||||
result.push({
|
||||
data: encode(this.data.substr(1, 5), "LLLLL"),
|
||||
text: this.text.substr(1, 5),
|
||||
options: {fontSize: this.fontSize}
|
||||
});
|
||||
|
||||
// Add the middle bits
|
||||
result.push({
|
||||
data: "01010",
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the right side
|
||||
result.push({
|
||||
data: encode(this.data.substr(6, 5), "RRRRR"),
|
||||
text: this.text.substr(6, 5),
|
||||
options: {fontSize: this.fontSize}
|
||||
});
|
||||
|
||||
// Add the end bits
|
||||
result.push({
|
||||
data: encode(this.data[11], "R") + "101",
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the last digit
|
||||
if(this.displayValue){
|
||||
result.push({
|
||||
data: "00000000",
|
||||
text: this.text.substr(11, 1),
|
||||
options: {textAlign: "right", fontSize: this.fontSize}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Calulate the checksum digit
|
||||
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Calculation_of_checksum_digit
|
||||
export function checksum(number){
|
||||
var result = 0;
|
||||
|
||||
var i;
|
||||
for(i = 1; i < 11; i += 2){
|
||||
result += parseInt(number[i]);
|
||||
}
|
||||
for(i = 0; i < 11; i += 2){
|
||||
result += parseInt(number[i]) * 3;
|
||||
}
|
||||
|
||||
return (10 - (result % 10)) % 10;
|
||||
}
|
||||
|
||||
export default UPC;
|
||||
@@ -0,0 +1,177 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/Universal_Product_Code#Encoding
|
||||
//
|
||||
// UPC-E documentation:
|
||||
// https://en.wikipedia.org/wiki/Universal_Product_Code#UPC-E
|
||||
|
||||
import encode from './encoder';
|
||||
import Barcode from "../Barcode.js";
|
||||
import { checksum } from './UPC.js';
|
||||
|
||||
const EXPANSIONS = [
|
||||
"XX00000XXX",
|
||||
"XX10000XXX",
|
||||
"XX20000XXX",
|
||||
"XXX00000XX",
|
||||
"XXXX00000X",
|
||||
"XXXXX00005",
|
||||
"XXXXX00006",
|
||||
"XXXXX00007",
|
||||
"XXXXX00008",
|
||||
"XXXXX00009"
|
||||
];
|
||||
|
||||
const PARITIES = [
|
||||
["EEEOOO", "OOOEEE"],
|
||||
["EEOEOO", "OOEOEE"],
|
||||
["EEOOEO", "OOEEOE"],
|
||||
["EEOOOE", "OOEEEO"],
|
||||
["EOEEOO", "OEOOEE"],
|
||||
["EOOEEO", "OEEOOE"],
|
||||
["EOOOEE", "OEEEOO"],
|
||||
["EOEOEO", "OEOEOE"],
|
||||
["EOEOOE", "OEOEEO"],
|
||||
["EOOEOE", "OEEOEO"]
|
||||
];
|
||||
|
||||
class UPCE extends Barcode{
|
||||
constructor(data, options){
|
||||
// Code may be 6 or 8 digits;
|
||||
// A 7 digit code is ambiguous as to whether the extra digit
|
||||
// is a UPC-A check or number system digit.
|
||||
super(data, options);
|
||||
this.isValid = false;
|
||||
if(data.search(/^[0-9]{6}$/) !== -1){
|
||||
this.middleDigits = data;
|
||||
this.upcA = expandToUPCA(data, "0");
|
||||
this.text = options.text ||
|
||||
`${this.upcA[0]}${data}${this.upcA[this.upcA.length - 1]}`;
|
||||
this.isValid = true;
|
||||
}
|
||||
else if(data.search(/^[01][0-9]{7}$/) !== -1){
|
||||
this.middleDigits = data.substring(1, data.length - 1);
|
||||
this.upcA = expandToUPCA(this.middleDigits, data[0]);
|
||||
|
||||
if(this.upcA[this.upcA.length - 1] === data[data.length - 1]){
|
||||
this.isValid = true;
|
||||
}
|
||||
else{
|
||||
// checksum mismatch
|
||||
return;
|
||||
}
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
|
||||
this.displayValue = options.displayValue;
|
||||
|
||||
// Make sure the font is not bigger than the space between the guard bars
|
||||
if(options.fontSize > options.width * 10){
|
||||
this.fontSize = options.width * 10;
|
||||
}
|
||||
else{
|
||||
this.fontSize = options.fontSize;
|
||||
}
|
||||
|
||||
// Make the guard bars go down half the way of the text
|
||||
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||
}
|
||||
|
||||
valid(){
|
||||
return this.isValid;
|
||||
}
|
||||
|
||||
encode(){
|
||||
if(this.options.flat){
|
||||
return this.flatEncoding();
|
||||
}
|
||||
else{
|
||||
return this.guardedEncoding();
|
||||
}
|
||||
}
|
||||
|
||||
flatEncoding(){
|
||||
var result = "";
|
||||
|
||||
result += "101";
|
||||
result += this.encodeMiddleDigits();
|
||||
result += "010101";
|
||||
|
||||
return {
|
||||
data: result,
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
guardedEncoding(){
|
||||
var result = [];
|
||||
|
||||
// Add the UPC-A number system digit beneath the quiet zone
|
||||
if(this.displayValue){
|
||||
result.push({
|
||||
data: "00000000",
|
||||
text: this.text[0],
|
||||
options: {textAlign: "left", fontSize: this.fontSize}
|
||||
});
|
||||
}
|
||||
|
||||
// Add the guard bars
|
||||
result.push({
|
||||
data: "101",
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the 6 UPC-E digits
|
||||
result.push({
|
||||
data: this.encodeMiddleDigits(),
|
||||
text: this.text.substring(1, 7),
|
||||
options: {fontSize: this.fontSize}
|
||||
});
|
||||
|
||||
// Add the end bits
|
||||
result.push({
|
||||
data: "010101",
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the UPC-A check digit beneath the quiet zone
|
||||
if(this.displayValue){
|
||||
result.push({
|
||||
data: "00000000",
|
||||
text: this.text[7],
|
||||
options: {textAlign: "right", fontSize: this.fontSize}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
encodeMiddleDigits() {
|
||||
const numberSystem = this.upcA[0];
|
||||
const checkDigit = this.upcA[this.upcA.length - 1];
|
||||
const parity = PARITIES[parseInt(checkDigit)][parseInt(numberSystem)];
|
||||
return encode(this.middleDigits, parity);
|
||||
}
|
||||
}
|
||||
|
||||
function expandToUPCA(middleDigits, numberSystem) {
|
||||
const lastUpcE = parseInt(middleDigits[middleDigits.length - 1]);
|
||||
const expansion = EXPANSIONS[lastUpcE];
|
||||
|
||||
let result = "";
|
||||
let digitIndex = 0;
|
||||
for(let i = 0; i < expansion.length; i++) {
|
||||
let c = expansion[i];
|
||||
if (c === 'X') {
|
||||
result += middleDigits[digitIndex++];
|
||||
} else {
|
||||
result += c;
|
||||
}
|
||||
}
|
||||
|
||||
result = `${numberSystem}${result}`;
|
||||
return `${result}${checksum(result)}`;
|
||||
}
|
||||
|
||||
export default UPCE;
|
||||
@@ -0,0 +1,41 @@
|
||||
// Standard start end and middle bits
|
||||
export const SIDE_BIN = '101';
|
||||
export const MIDDLE_BIN = '01010';
|
||||
|
||||
export const BINARIES = {
|
||||
'L': [ // The L (left) type of encoding
|
||||
'0001101', '0011001', '0010011', '0111101', '0100011',
|
||||
'0110001', '0101111', '0111011', '0110111', '0001011'
|
||||
],
|
||||
'G': [ // The G type of encoding
|
||||
'0100111', '0110011', '0011011', '0100001', '0011101',
|
||||
'0111001', '0000101', '0010001', '0001001', '0010111'
|
||||
],
|
||||
'R': [ // The R (right) type of encoding
|
||||
'1110010', '1100110', '1101100', '1000010', '1011100',
|
||||
'1001110', '1010000', '1000100', '1001000', '1110100'
|
||||
],
|
||||
'O': [ // The O (odd) encoding for UPC-E
|
||||
'0001101', '0011001', '0010011', '0111101', '0100011',
|
||||
'0110001', '0101111', '0111011', '0110111', '0001011'
|
||||
],
|
||||
'E': [ // The E (even) encoding for UPC-E
|
||||
'0100111', '0110011', '0011011', '0100001', '0011101',
|
||||
'0111001', '0000101', '0010001', '0001001', '0010111'
|
||||
]
|
||||
};
|
||||
|
||||
// Define the EAN-2 structure
|
||||
export const EAN2_STRUCTURE = ['LL', 'LG', 'GL', 'GG'];
|
||||
|
||||
// Define the EAN-5 structure
|
||||
export const EAN5_STRUCTURE = [
|
||||
'GGLLL', 'GLGLL', 'GLLGL', 'GLLLG', 'LGGLL',
|
||||
'LLGGL', 'LLLGG', 'LGLGL', 'LGLLG', 'LLGLG'
|
||||
];
|
||||
|
||||
// Define the EAN-13 structure
|
||||
export const EAN13_STRUCTURE = [
|
||||
'LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG',
|
||||
'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL'
|
||||
];
|
||||
@@ -0,0 +1,20 @@
|
||||
import { BINARIES } from './constants';
|
||||
|
||||
// Encode data string
|
||||
const encode = (data, structure, separator) => {
|
||||
let encoded = data
|
||||
.split('')
|
||||
.map((val, idx) => BINARIES[structure[idx]])
|
||||
.map((val, idx) => val ? val[data[idx]] : '');
|
||||
|
||||
if (separator) {
|
||||
const last = data.length - 1;
|
||||
encoded = encoded.map((val, idx) => (
|
||||
idx < last ? val + separator : val
|
||||
));
|
||||
}
|
||||
|
||||
return encoded.join('');
|
||||
};
|
||||
|
||||
export default encode;
|
||||
@@ -0,0 +1,8 @@
|
||||
import EAN13 from './EAN13.js';
|
||||
import EAN8 from './EAN8.js';
|
||||
import EAN5 from './EAN5.js';
|
||||
import EAN2 from './EAN2.js';
|
||||
import UPC from './UPC.js';
|
||||
import UPCE from './UPCE.js';
|
||||
|
||||
export {EAN13, EAN8, EAN5, EAN2, UPC, UPCE};
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import Barcode from "../Barcode.js";
|
||||
|
||||
class GenericBarcode extends Barcode{
|
||||
constructor(data, options){
|
||||
super(data, options); // Sets this.data and this.text
|
||||
}
|
||||
|
||||
// Return the corresponding binary numbers for the data provided
|
||||
encode(){
|
||||
return {
|
||||
data: "10101010101010101010101010101010101010101",
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
// Resturn true/false if the string provided is valid for this encoder
|
||||
valid(){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export {GenericBarcode};
|
||||
@@ -0,0 +1,37 @@
|
||||
import { START_BIN, END_BIN, BINARIES } from './constants';
|
||||
import Barcode from '../Barcode';
|
||||
|
||||
class ITF extends Barcode {
|
||||
|
||||
valid() {
|
||||
return this.data.search(/^([0-9]{2})+$/) !== -1;
|
||||
}
|
||||
|
||||
encode() {
|
||||
// Calculate all the digit pairs
|
||||
const encoded = this.data
|
||||
.match(/.{2}/g)
|
||||
.map(pair => this.encodePair(pair))
|
||||
.join('');
|
||||
|
||||
return {
|
||||
data: START_BIN + encoded + END_BIN,
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate the data of a number pair
|
||||
encodePair(pair) {
|
||||
const second = BINARIES[pair[1]];
|
||||
|
||||
return BINARIES[pair[0]]
|
||||
.split('')
|
||||
.map((first, idx) => (
|
||||
(first === '1' ? '111' : '1') +
|
||||
(second[idx] === '1' ? '000' : '0')
|
||||
))
|
||||
.join('');
|
||||
}
|
||||
}
|
||||
|
||||
export default ITF;
|
||||
@@ -0,0 +1,33 @@
|
||||
import ITF from './ITF';
|
||||
|
||||
// Calculate the checksum digit
|
||||
const checksum = (data) => {
|
||||
const res = data
|
||||
.substr(0, 13)
|
||||
.split('')
|
||||
.map(num => parseInt(num, 10))
|
||||
.reduce((sum, n, idx) => sum + (n * (3 - (idx % 2) * 2)), 0);
|
||||
|
||||
return Math.ceil(res / 10) * 10 - res;
|
||||
};
|
||||
|
||||
class ITF14 extends ITF {
|
||||
|
||||
constructor(data, options) {
|
||||
// Add checksum if it does not exist
|
||||
if (data.search(/^[0-9]{13}$/) !== -1) {
|
||||
data += checksum(data);
|
||||
}
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return (
|
||||
this.data.search(/^[0-9]{14}$/) !== -1 &&
|
||||
+this.data[13] === checksum(this.data)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ITF14;
|
||||
@@ -0,0 +1,7 @@
|
||||
export const START_BIN = '1010';
|
||||
export const END_BIN = '11101';
|
||||
|
||||
export const BINARIES = [
|
||||
'00110', '10001', '01001', '11000', '00101',
|
||||
'10100', '01100', '00011', '10010', '01010',
|
||||
];
|
||||
@@ -0,0 +1,4 @@
|
||||
import ITF from './ITF';
|
||||
import ITF14 from './ITF14';
|
||||
|
||||
export { ITF, ITF14 };
|
||||
@@ -0,0 +1,48 @@
|
||||
// Encoding documentation
|
||||
// https://en.wikipedia.org/wiki/MSI_Barcode#Character_set_and_binary_lookup
|
||||
|
||||
import Barcode from "../Barcode.js";
|
||||
|
||||
class MSI extends Barcode{
|
||||
constructor(data, options){
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
encode(){
|
||||
// Start bits
|
||||
var ret = "110";
|
||||
|
||||
for(var i = 0; i < this.data.length; i++){
|
||||
// Convert the character to binary (always 4 binary digits)
|
||||
var digit = parseInt(this.data[i]);
|
||||
var bin = digit.toString(2);
|
||||
bin = addZeroes(bin, 4 - bin.length);
|
||||
|
||||
// Add 100 for every zero and 110 for every 1
|
||||
for(var b = 0; b < bin.length; b++){
|
||||
ret += bin[b] == "0" ? "100" : "110";
|
||||
}
|
||||
}
|
||||
|
||||
// End bits
|
||||
ret += "1001";
|
||||
|
||||
return {
|
||||
data: ret,
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
valid(){
|
||||
return this.data.search(/^[0-9]+$/) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
function addZeroes(number, n){
|
||||
for(var i = 0; i < n; i++){
|
||||
number = "0" + number;
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
export default MSI;
|
||||
@@ -0,0 +1,10 @@
|
||||
import MSI from './MSI.js';
|
||||
import {mod10} from './checksums.js';
|
||||
|
||||
class MSI10 extends MSI{
|
||||
constructor(data, options){
|
||||
super(data + mod10(data), options);
|
||||
}
|
||||
}
|
||||
|
||||
export default MSI10;
|
||||
@@ -0,0 +1,12 @@
|
||||
import MSI from './MSI.js';
|
||||
import {mod10} from './checksums.js';
|
||||
|
||||
class MSI1010 extends MSI{
|
||||
constructor(data, options){
|
||||
data += mod10(data);
|
||||
data += mod10(data);
|
||||
super(data, options);
|
||||
}
|
||||
}
|
||||
|
||||
export default MSI1010;
|
||||
@@ -0,0 +1,10 @@
|
||||
import MSI from './MSI.js';
|
||||
import {mod11} from './checksums.js';
|
||||
|
||||
class MSI11 extends MSI{
|
||||
constructor(data, options){
|
||||
super(data + mod11(data), options);
|
||||
}
|
||||
}
|
||||
|
||||
export default MSI11;
|
||||
@@ -0,0 +1,12 @@
|
||||
import MSI from './MSI.js';
|
||||
import {mod10, mod11} from './checksums.js';
|
||||
|
||||
class MSI1110 extends MSI{
|
||||
constructor(data, options){
|
||||
data += mod11(data);
|
||||
data += mod10(data);
|
||||
super(data, options);
|
||||
}
|
||||
}
|
||||
|
||||
export default MSI1110;
|
||||
@@ -0,0 +1,23 @@
|
||||
export function mod10(number){
|
||||
var sum = 0;
|
||||
for(var i = 0; i < number.length; i++){
|
||||
var n = parseInt(number[i]);
|
||||
if((i + number.length) % 2 === 0){
|
||||
sum += n;
|
||||
}
|
||||
else{
|
||||
sum += (n * 2) % 10 + Math.floor((n * 2) / 10);
|
||||
}
|
||||
}
|
||||
return (10 - (sum % 10)) % 10;
|
||||
}
|
||||
|
||||
export function mod11(number){
|
||||
var sum = 0;
|
||||
var weights = [2, 3, 4, 5, 6, 7];
|
||||
for(var i = 0; i < number.length; i++){
|
||||
var n = parseInt(number[number.length - 1 - i]);
|
||||
sum += weights[i % weights.length] * n;
|
||||
}
|
||||
return (11 - (sum % 11)) % 11;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import MSI from './MSI.js';
|
||||
import MSI10 from './MSI10.js';
|
||||
import MSI11 from './MSI11.js';
|
||||
import MSI1010 from './MSI1010.js';
|
||||
import MSI1110 from './MSI1110.js';
|
||||
|
||||
export {MSI, MSI10, MSI11, MSI1010, MSI1110};
|
||||
@@ -0,0 +1,63 @@
|
||||
// Encoding specification:
|
||||
// http://www.barcodeisland.com/codabar.phtml
|
||||
|
||||
import Barcode from "../Barcode.js";
|
||||
|
||||
class codabar extends Barcode{
|
||||
constructor(data, options){
|
||||
if (data.search(/^[0-9\-\$\:\.\+\/]+$/) === 0) {
|
||||
data = "A" + data + "A";
|
||||
}
|
||||
|
||||
super(data.toUpperCase(), options);
|
||||
|
||||
this.text = this.options.text || this.text.replace(/[A-D]/g, '');
|
||||
}
|
||||
|
||||
valid(){
|
||||
return this.data.search(/^[A-D][0-9\-\$\:\.\+\/]+[A-D]$/) !== -1;
|
||||
}
|
||||
|
||||
encode(){
|
||||
var result = [];
|
||||
var encodings = this.getEncodings();
|
||||
for(var i = 0; i < this.data.length; i++){
|
||||
result.push(encodings[this.data.charAt(i)]);
|
||||
// for all characters except the last, append a narrow-space ("0")
|
||||
if (i !== this.data.length - 1) {
|
||||
result.push("0");
|
||||
}
|
||||
}
|
||||
return {
|
||||
text: this.text,
|
||||
data: result.join('')
|
||||
};
|
||||
}
|
||||
|
||||
getEncodings(){
|
||||
return {
|
||||
"0": "101010011",
|
||||
"1": "101011001",
|
||||
"2": "101001011",
|
||||
"3": "110010101",
|
||||
"4": "101101001",
|
||||
"5": "110101001",
|
||||
"6": "100101011",
|
||||
"7": "100101101",
|
||||
"8": "100110101",
|
||||
"9": "110100101",
|
||||
"-": "101001101",
|
||||
"$": "101100101",
|
||||
":": "1101011011",
|
||||
"/": "1101101011",
|
||||
".": "1101101101",
|
||||
"+": "1011011011",
|
||||
"A": "1011001001",
|
||||
"B": "1001001011",
|
||||
"C": "1010010011",
|
||||
"D": "1010011001"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export {codabar};
|
||||
@@ -0,0 +1,22 @@
|
||||
import {CODE39} from './CODE39/';
|
||||
import {CODE128, CODE128A, CODE128B, CODE128C} from './CODE128/';
|
||||
import {EAN13, EAN8, EAN5, EAN2, UPC, UPCE} from './EAN_UPC/';
|
||||
import {ITF, ITF14} from './ITF/';
|
||||
import {MSI, MSI10, MSI11, MSI1010, MSI1110} from './MSI/';
|
||||
import {pharmacode} from './pharmacode/';
|
||||
import {codabar} from './codabar';
|
||||
import {CODE93, CODE93FullASCII} from './CODE93/';
|
||||
import {GenericBarcode} from './GenericBarcode/';
|
||||
|
||||
export default {
|
||||
CODE39,
|
||||
CODE128, CODE128A, CODE128B, CODE128C,
|
||||
EAN13, EAN8, EAN5, EAN2, UPC, UPCE,
|
||||
ITF14,
|
||||
ITF,
|
||||
MSI, MSI10, MSI11, MSI1010, MSI1110,
|
||||
pharmacode,
|
||||
codabar,
|
||||
CODE93, CODE93FullASCII,
|
||||
GenericBarcode
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
// Encoding documentation
|
||||
// http://www.gomaro.ch/ftproot/Laetus_PHARMA-CODE.pdf
|
||||
|
||||
import Barcode from "../Barcode.js";
|
||||
|
||||
class pharmacode extends Barcode{
|
||||
constructor(data, options){
|
||||
super(data, options);
|
||||
this.number = parseInt(data, 10);
|
||||
}
|
||||
|
||||
encode(){
|
||||
var z = this.number;
|
||||
var result = "";
|
||||
|
||||
// http://i.imgur.com/RMm4UDJ.png
|
||||
// (source: http://www.gomaro.ch/ftproot/Laetus_PHARMA-CODE.pdf, page: 34)
|
||||
while(!isNaN(z) && z != 0){
|
||||
if(z % 2 === 0){ // Even
|
||||
result = "11100" + result;
|
||||
z = (z - 2) / 2;
|
||||
}
|
||||
else{ // Odd
|
||||
result = "100" + result;
|
||||
z = (z - 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the two last zeroes
|
||||
result = result.slice(0, -2);
|
||||
|
||||
return {
|
||||
data: result,
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
valid(){
|
||||
return this.number >= 3 && this.number <= 131070;
|
||||
}
|
||||
}
|
||||
|
||||
export {pharmacode};
|
||||
@@ -0,0 +1,39 @@
|
||||
/*eslint no-console: 0 */
|
||||
|
||||
class ErrorHandler{
|
||||
constructor(api){
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
handleCatch(e){
|
||||
// If babel supported extending of Error in a correct way instanceof would be used here
|
||||
if(e.name === "InvalidInputException"){
|
||||
if(this.api._options.valid !== this.api._defaults.valid){
|
||||
this.api._options.valid(false);
|
||||
}
|
||||
else{
|
||||
throw e.message;
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw e;
|
||||
}
|
||||
|
||||
this.api.render = function(){};
|
||||
}
|
||||
|
||||
wrapBarcodeCall(func){
|
||||
try{
|
||||
var result = func(...arguments);
|
||||
this.api._options.valid(true);
|
||||
return result;
|
||||
}
|
||||
catch(e){
|
||||
this.handleCatch(e);
|
||||
|
||||
return this.api;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorHandler;
|
||||
@@ -0,0 +1,29 @@
|
||||
class InvalidInputException extends Error{
|
||||
constructor(symbology, input) {
|
||||
super();
|
||||
this.name = "InvalidInputException";
|
||||
|
||||
this.symbology = symbology;
|
||||
this.input = input;
|
||||
|
||||
this.message = '"' + this.input + '" is not a valid input for ' + this.symbology;
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidElementException extends Error{
|
||||
constructor() {
|
||||
super();
|
||||
this.name = "InvalidElementException";
|
||||
this.message = "Not supported type to render on";
|
||||
}
|
||||
}
|
||||
|
||||
class NoElementException extends Error{
|
||||
constructor() {
|
||||
super();
|
||||
this.name = "NoElementException";
|
||||
this.message = "No element to render on.";
|
||||
}
|
||||
}
|
||||
|
||||
export {InvalidInputException, InvalidElementException, NoElementException};
|
||||
@@ -0,0 +1,11 @@
|
||||
export default fixOptions;
|
||||
|
||||
function fixOptions(options){
|
||||
// Fix the margins
|
||||
options.marginTop = options.marginTop || options.margin;
|
||||
options.marginBottom = options.marginBottom || options.margin;
|
||||
options.marginRight = options.marginRight || options.margin;
|
||||
options.marginLeft = options.marginLeft || options.margin;
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import optionsFromStrings from "./optionsFromStrings.js";
|
||||
import defaults from "../options/defaults.js";
|
||||
|
||||
function getOptionsFromElement(element){
|
||||
var options = {};
|
||||
for(var property in defaults){
|
||||
if(defaults.hasOwnProperty(property)){
|
||||
// jsbarcode-*
|
||||
if(element.hasAttribute("jsbarcode-" + property.toLowerCase())){
|
||||
options[property] = element.getAttribute("jsbarcode-" + property.toLowerCase());
|
||||
}
|
||||
|
||||
// data-*
|
||||
if(element.hasAttribute("data-" + property.toLowerCase())){
|
||||
options[property] = element.getAttribute("data-" + property.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options["value"] = element.getAttribute("jsbarcode-value") || element.getAttribute("data-value");
|
||||
|
||||
// Since all atributes are string they need to be converted to integers
|
||||
options = optionsFromStrings(options);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export default getOptionsFromElement;
|
||||
@@ -0,0 +1,103 @@
|
||||
/* global HTMLImageElement */
|
||||
/* global HTMLCanvasElement */
|
||||
/* global SVGElement */
|
||||
|
||||
import getOptionsFromElement from "./getOptionsFromElement.js";
|
||||
import renderers from "../renderers";
|
||||
|
||||
import {InvalidElementException} from "../exceptions/exceptions.js";
|
||||
|
||||
// Takes an element and returns an object with information about how
|
||||
// it should be rendered
|
||||
// This could also return an array with these objects
|
||||
// {
|
||||
// element: The element that the renderer should draw on
|
||||
// renderer: The name of the renderer
|
||||
// afterRender (optional): If something has to done after the renderer
|
||||
// completed, calls afterRender (function)
|
||||
// options (optional): Options that can be defined in the element
|
||||
// }
|
||||
|
||||
function getRenderProperties(element){
|
||||
// If the element is a string, query select call again
|
||||
if(typeof element === "string"){
|
||||
return querySelectedRenderProperties(element);
|
||||
}
|
||||
// If element is array. Recursivly call with every object in the array
|
||||
else if(Array.isArray(element)){
|
||||
var returnArray = [];
|
||||
for(let i = 0; i < element.length; i++){
|
||||
returnArray.push(getRenderProperties(element[i]));
|
||||
}
|
||||
return returnArray;
|
||||
}
|
||||
// If element, render on canvas and set the uri as src
|
||||
else if(typeof HTMLCanvasElement !== 'undefined' && element instanceof HTMLImageElement){
|
||||
return newCanvasRenderProperties(element);
|
||||
}
|
||||
// If SVG
|
||||
else if(
|
||||
(element && element.nodeName && element.nodeName.toLowerCase() === 'svg') ||
|
||||
(typeof SVGElement !== 'undefined' && element instanceof SVGElement)
|
||||
){
|
||||
return {
|
||||
element: element,
|
||||
options: getOptionsFromElement(element),
|
||||
renderer: renderers.SVGRenderer
|
||||
};
|
||||
}
|
||||
// If canvas (in browser)
|
||||
else if(typeof HTMLCanvasElement !== 'undefined' && element instanceof HTMLCanvasElement){
|
||||
return {
|
||||
element: element,
|
||||
options: getOptionsFromElement(element),
|
||||
renderer: renderers.CanvasRenderer
|
||||
};
|
||||
}
|
||||
|
||||
// If canvas (in node)
|
||||
else if(element && element.getContext){
|
||||
return {
|
||||
element: element,
|
||||
renderer: renderers.CanvasRenderer
|
||||
};
|
||||
}
|
||||
else if(element && typeof element === 'object' && !element.nodeName) {
|
||||
return {
|
||||
element: element,
|
||||
renderer: renderers.ObjectRenderer
|
||||
};
|
||||
}
|
||||
else{
|
||||
throw new InvalidElementException();
|
||||
}
|
||||
}
|
||||
|
||||
function querySelectedRenderProperties(string){
|
||||
var selector = document.querySelectorAll(string);
|
||||
if(selector.length === 0){
|
||||
return undefined;
|
||||
}
|
||||
else{
|
||||
let returnArray = [];
|
||||
for(let i = 0; i < selector.length; i++){
|
||||
returnArray.push(getRenderProperties(selector[i]));
|
||||
}
|
||||
return returnArray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function newCanvasRenderProperties(imgElement){
|
||||
var canvas = document.createElement('canvas');
|
||||
return {
|
||||
element: canvas,
|
||||
options: getOptionsFromElement(imgElement),
|
||||
renderer: renderers.CanvasRenderer,
|
||||
afterRender: function(){
|
||||
imgElement.setAttribute("src", canvas.toDataURL());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default getRenderProperties;
|
||||
@@ -0,0 +1,22 @@
|
||||
export default linearizeEncodings;
|
||||
|
||||
// Encodings can be nestled like [[1-1, 1-2], 2, [3-1, 3-2]
|
||||
// Convert to [1-1, 1-2, 2, 3-1, 3-2]
|
||||
function linearizeEncodings(encodings){
|
||||
var linearEncodings = [];
|
||||
function nextLevel(encoded){
|
||||
if(Array.isArray(encoded)){
|
||||
for(let i = 0; i < encoded.length; i++){
|
||||
nextLevel(encoded[i]);
|
||||
}
|
||||
}
|
||||
else{
|
||||
encoded.text = encoded.text || "";
|
||||
encoded.data = encoded.data || "";
|
||||
linearEncodings.push(encoded);
|
||||
}
|
||||
}
|
||||
nextLevel(encodings);
|
||||
|
||||
return linearEncodings;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export default (old, replaceObj) => ({ ...old, ...replaceObj });
|
||||
@@ -0,0 +1,31 @@
|
||||
export default optionsFromStrings;
|
||||
|
||||
// Convert string to integers/booleans where it should be
|
||||
function optionsFromStrings(options){
|
||||
var intOptions = [
|
||||
"width",
|
||||
"height",
|
||||
"textMargin",
|
||||
"fontSize",
|
||||
"margin",
|
||||
"marginTop",
|
||||
"marginBottom",
|
||||
"marginLeft",
|
||||
"marginRight"
|
||||
];
|
||||
|
||||
for(var intOption in intOptions){
|
||||
if(intOptions.hasOwnProperty(intOption)){
|
||||
intOption = intOptions[intOption];
|
||||
if(typeof options[intOption] === "string"){
|
||||
options[intOption] = parseInt(options[intOption], 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(typeof options["displayValue"] === "string"){
|
||||
options["displayValue"] = (options["displayValue"] != "false");
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
var defaults = {
|
||||
width: 2,
|
||||
height: 100,
|
||||
format: "auto",
|
||||
displayValue: true,
|
||||
fontOptions: "",
|
||||
font: "monospace",
|
||||
text: undefined,
|
||||
textAlign: "center",
|
||||
textPosition: "bottom",
|
||||
textMargin: 2,
|
||||
fontSize: 20,
|
||||
background: "#ffffff",
|
||||
lineColor: "#000000",
|
||||
margin: 10,
|
||||
marginTop: undefined,
|
||||
marginBottom: undefined,
|
||||
marginLeft: undefined,
|
||||
marginRight: undefined,
|
||||
valid: function(){}
|
||||
};
|
||||
|
||||
export default defaults;
|
||||
@@ -0,0 +1,137 @@
|
||||
import merge from "../help/merge.js";
|
||||
import {calculateEncodingAttributes, getTotalWidthOfEncodings, getMaximumHeightOfEncodings} from "./shared.js";
|
||||
|
||||
class CanvasRenderer{
|
||||
constructor(canvas, encodings, options){
|
||||
this.canvas = canvas;
|
||||
this.encodings = encodings;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
render(){
|
||||
// Abort if the browser does not support HTML5 canvas
|
||||
if (!this.canvas.getContext) {
|
||||
throw new Error('The browser does not support canvas.');
|
||||
}
|
||||
|
||||
this.prepareCanvas();
|
||||
for(let i = 0; i < this.encodings.length; i++){
|
||||
var encodingOptions = merge(this.options, this.encodings[i].options);
|
||||
|
||||
this.drawCanvasBarcode(encodingOptions, this.encodings[i]);
|
||||
this.drawCanvasText(encodingOptions, this.encodings[i]);
|
||||
|
||||
this.moveCanvasDrawing(this.encodings[i]);
|
||||
}
|
||||
|
||||
this.restoreCanvas();
|
||||
}
|
||||
|
||||
prepareCanvas(){
|
||||
// Get the canvas context
|
||||
var ctx = this.canvas.getContext("2d");
|
||||
|
||||
ctx.save();
|
||||
|
||||
calculateEncodingAttributes(this.encodings, this.options, ctx);
|
||||
var totalWidth = getTotalWidthOfEncodings(this.encodings);
|
||||
var maxHeight = getMaximumHeightOfEncodings(this.encodings);
|
||||
|
||||
this.canvas.width = totalWidth + this.options.marginLeft + this.options.marginRight;
|
||||
|
||||
this.canvas.height = maxHeight;
|
||||
|
||||
// Paint the canvas
|
||||
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
if(this.options.background){
|
||||
ctx.fillStyle = this.options.background;
|
||||
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
ctx.translate(this.options.marginLeft, 0);
|
||||
}
|
||||
|
||||
drawCanvasBarcode(options, encoding){
|
||||
// Get the canvas context
|
||||
var ctx = this.canvas.getContext("2d");
|
||||
|
||||
var binary = encoding.data;
|
||||
|
||||
// Creates the barcode out of the encoded binary
|
||||
var yFrom;
|
||||
if(options.textPosition == "top"){
|
||||
yFrom = options.marginTop + options.fontSize + options.textMargin;
|
||||
}
|
||||
else{
|
||||
yFrom = options.marginTop;
|
||||
}
|
||||
|
||||
ctx.fillStyle = options.lineColor;
|
||||
|
||||
for(var b = 0; b < binary.length; b++){
|
||||
var x = b * options.width + encoding.barcodePadding;
|
||||
|
||||
if(binary[b] === "1"){
|
||||
ctx.fillRect(x, yFrom, options.width, options.height);
|
||||
}
|
||||
else if(binary[b]){
|
||||
ctx.fillRect(x, yFrom, options.width, options.height * binary[b]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawCanvasText(options, encoding){
|
||||
// Get the canvas context
|
||||
var ctx = this.canvas.getContext("2d");
|
||||
|
||||
var font = options.fontOptions + " " + options.fontSize + "px " + options.font;
|
||||
|
||||
// Draw the text if displayValue is set
|
||||
if(options.displayValue){
|
||||
var x, y;
|
||||
|
||||
if(options.textPosition == "top"){
|
||||
y = options.marginTop + options.fontSize - options.textMargin;
|
||||
}
|
||||
else{
|
||||
y = options.height + options.textMargin + options.marginTop + options.fontSize;
|
||||
}
|
||||
|
||||
ctx.font = font;
|
||||
|
||||
// Draw the text in the correct X depending on the textAlign option
|
||||
if(options.textAlign == "left" || encoding.barcodePadding > 0){
|
||||
x = 0;
|
||||
ctx.textAlign = 'left';
|
||||
}
|
||||
else if(options.textAlign == "right"){
|
||||
x = encoding.width - 1;
|
||||
ctx.textAlign = 'right';
|
||||
}
|
||||
// In all other cases, center the text
|
||||
else{
|
||||
x = encoding.width / 2;
|
||||
ctx.textAlign = 'center';
|
||||
}
|
||||
|
||||
ctx.fillText(encoding.text, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
moveCanvasDrawing(encoding){
|
||||
var ctx = this.canvas.getContext("2d");
|
||||
|
||||
ctx.translate(encoding.width, 0);
|
||||
}
|
||||
|
||||
restoreCanvas(){
|
||||
// Get the canvas context
|
||||
var ctx = this.canvas.getContext("2d");
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
export default CanvasRenderer;
|
||||
@@ -0,0 +1,5 @@
|
||||
import CanvasRenderer from './canvas.js';
|
||||
import SVGRenderer from './svg.js';
|
||||
import ObjectRenderer from './object.js';
|
||||
|
||||
export default {CanvasRenderer, SVGRenderer, ObjectRenderer};
|
||||
@@ -0,0 +1,14 @@
|
||||
class ObjectRenderer {
|
||||
constructor(object, encodings, options) {
|
||||
this.object = object;
|
||||
this.encodings = encodings;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.object.encodings = this.encodings;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default ObjectRenderer;
|
||||
@@ -0,0 +1,94 @@
|
||||
import merge from "../help/merge.js";
|
||||
|
||||
function getEncodingHeight(encoding, options){
|
||||
return options.height +
|
||||
((options.displayValue && encoding.text.length > 0) ? options.fontSize + options.textMargin : 0) +
|
||||
options.marginTop +
|
||||
options.marginBottom;
|
||||
}
|
||||
|
||||
function getBarcodePadding(textWidth, barcodeWidth, options){
|
||||
if(options.displayValue && barcodeWidth < textWidth){
|
||||
if(options.textAlign == "center"){
|
||||
return Math.floor((textWidth - barcodeWidth) / 2);
|
||||
}
|
||||
else if(options.textAlign == "left"){
|
||||
return 0;
|
||||
}
|
||||
else if(options.textAlign == "right"){
|
||||
return Math.floor(textWidth - barcodeWidth);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function calculateEncodingAttributes(encodings, barcodeOptions, context){
|
||||
for(let i = 0; i < encodings.length; i++){
|
||||
var encoding = encodings[i];
|
||||
var options = merge(barcodeOptions, encoding.options);
|
||||
|
||||
// Calculate the width of the encoding
|
||||
var textWidth;
|
||||
if(options.displayValue){
|
||||
textWidth = messureText(encoding.text, options, context);
|
||||
}
|
||||
else{
|
||||
textWidth = 0;
|
||||
}
|
||||
|
||||
var barcodeWidth = encoding.data.length * options.width;
|
||||
encoding.width = Math.ceil(Math.max(textWidth, barcodeWidth));
|
||||
|
||||
encoding.height = getEncodingHeight(encoding, options);
|
||||
|
||||
encoding.barcodePadding = getBarcodePadding(textWidth, barcodeWidth, options);
|
||||
}
|
||||
}
|
||||
|
||||
function getTotalWidthOfEncodings(encodings){
|
||||
var totalWidth = 0;
|
||||
for(let i = 0; i < encodings.length; i++){
|
||||
totalWidth += encodings[i].width;
|
||||
}
|
||||
return totalWidth;
|
||||
}
|
||||
|
||||
function getMaximumHeightOfEncodings(encodings){
|
||||
var maxHeight = 0;
|
||||
for(let i = 0; i < encodings.length; i++){
|
||||
if(encodings[i].height > maxHeight){
|
||||
maxHeight = encodings[i].height;
|
||||
}
|
||||
}
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
function messureText(string, options, context){
|
||||
var ctx;
|
||||
|
||||
if(context){
|
||||
ctx = context;
|
||||
}
|
||||
else if(typeof document !== "undefined"){
|
||||
ctx = document.createElement("canvas").getContext("2d");
|
||||
}
|
||||
else{
|
||||
// If the text cannot be messured we will return 0.
|
||||
// This will make some barcode with big text render incorrectly
|
||||
return 0;
|
||||
}
|
||||
ctx.font = options.fontOptions + " " + options.fontSize + "px " + options.font;
|
||||
|
||||
// Calculate the width of the encoding
|
||||
var measureTextResult = ctx.measureText(string);
|
||||
if (!measureTextResult) {
|
||||
// Some implementations don't implement measureText and return undefined.
|
||||
// If the text cannot be measured we will return 0.
|
||||
// This will make some barcode with big text render incorrectly
|
||||
return 0;
|
||||
}
|
||||
var size = measureTextResult.width;
|
||||
return size;
|
||||
}
|
||||
|
||||
export {getMaximumHeightOfEncodings, getEncodingHeight, getBarcodePadding, calculateEncodingAttributes, getTotalWidthOfEncodings};
|
||||
@@ -0,0 +1,171 @@
|
||||
import merge from "../help/merge.js";
|
||||
import {calculateEncodingAttributes, getTotalWidthOfEncodings, getMaximumHeightOfEncodings} from "./shared.js";
|
||||
|
||||
var svgns = "http://www.w3.org/2000/svg";
|
||||
|
||||
class SVGRenderer{
|
||||
constructor(svg, encodings, options){
|
||||
this.svg = svg;
|
||||
this.encodings = encodings;
|
||||
this.options = options;
|
||||
this.document = options.xmlDocument || document;
|
||||
}
|
||||
|
||||
render(){
|
||||
var currentX = this.options.marginLeft;
|
||||
|
||||
this.prepareSVG();
|
||||
for(let i = 0; i < this.encodings.length; i++){
|
||||
var encoding = this.encodings[i];
|
||||
var encodingOptions = merge(this.options, encoding.options);
|
||||
|
||||
var group = this.createGroup(currentX, encodingOptions.marginTop, this.svg);
|
||||
|
||||
this.setGroupOptions(group, encodingOptions);
|
||||
|
||||
this.drawSvgBarcode(group, encodingOptions, encoding);
|
||||
this.drawSVGText(group, encodingOptions, encoding);
|
||||
|
||||
currentX += encoding.width;
|
||||
}
|
||||
}
|
||||
|
||||
prepareSVG(){
|
||||
// Clear the SVG
|
||||
while (this.svg.firstChild) {
|
||||
this.svg.removeChild(this.svg.firstChild);
|
||||
}
|
||||
|
||||
calculateEncodingAttributes(this.encodings, this.options);
|
||||
var totalWidth = getTotalWidthOfEncodings(this.encodings);
|
||||
var maxHeight = getMaximumHeightOfEncodings(this.encodings);
|
||||
|
||||
var width = totalWidth + this.options.marginLeft + this.options.marginRight;
|
||||
this.setSvgAttributes(width, maxHeight);
|
||||
|
||||
if(this.options.background){
|
||||
this.drawRect(0, 0, width, maxHeight, this.svg).setAttribute(
|
||||
"style", "fill:" + this.options.background + ";"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
drawSvgBarcode(parent, options, encoding){
|
||||
var binary = encoding.data;
|
||||
|
||||
// Creates the barcode out of the encoded binary
|
||||
var yFrom;
|
||||
if(options.textPosition == "top"){
|
||||
yFrom = options.fontSize + options.textMargin;
|
||||
}
|
||||
else{
|
||||
yFrom = 0;
|
||||
}
|
||||
|
||||
var barWidth = 0;
|
||||
var x = 0;
|
||||
for(var b = 0; b < binary.length; b++){
|
||||
x = b * options.width + encoding.barcodePadding;
|
||||
|
||||
if(binary[b] === "1"){
|
||||
barWidth++;
|
||||
}
|
||||
else if(barWidth > 0){
|
||||
this.drawRect(x - options.width * barWidth, yFrom, options.width * barWidth, options.height, parent);
|
||||
barWidth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Last draw is needed since the barcode ends with 1
|
||||
if(barWidth > 0){
|
||||
this.drawRect(x - options.width * (barWidth - 1), yFrom, options.width * barWidth, options.height, parent);
|
||||
}
|
||||
}
|
||||
|
||||
drawSVGText(parent, options, encoding){
|
||||
var textElem = this.document.createElementNS(svgns, 'text');
|
||||
|
||||
// Draw the text if displayValue is set
|
||||
if(options.displayValue){
|
||||
var x, y;
|
||||
|
||||
textElem.setAttribute("style",
|
||||
"font:" + options.fontOptions + " " + options.fontSize + "px " + options.font
|
||||
);
|
||||
|
||||
if(options.textPosition == "top"){
|
||||
y = options.fontSize - options.textMargin;
|
||||
}
|
||||
else{
|
||||
y = options.height + options.textMargin + options.fontSize;
|
||||
}
|
||||
|
||||
// Draw the text in the correct X depending on the textAlign option
|
||||
if(options.textAlign == "left" || encoding.barcodePadding > 0){
|
||||
x = 0;
|
||||
textElem.setAttribute("text-anchor", "start");
|
||||
}
|
||||
else if(options.textAlign == "right"){
|
||||
x = encoding.width - 1;
|
||||
textElem.setAttribute("text-anchor", "end");
|
||||
}
|
||||
// In all other cases, center the text
|
||||
else{
|
||||
x = encoding.width / 2;
|
||||
textElem.setAttribute("text-anchor", "middle");
|
||||
}
|
||||
|
||||
textElem.setAttribute("x", x);
|
||||
textElem.setAttribute("y", y);
|
||||
|
||||
textElem.appendChild(this.document.createTextNode(encoding.text));
|
||||
|
||||
parent.appendChild(textElem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setSvgAttributes(width, height){
|
||||
var svg = this.svg;
|
||||
svg.setAttribute("width", width + "px");
|
||||
svg.setAttribute("height", height + "px");
|
||||
svg.setAttribute("x", "0px");
|
||||
svg.setAttribute("y", "0px");
|
||||
svg.setAttribute("viewBox", "0 0 " + width + " " + height);
|
||||
|
||||
svg.setAttribute("xmlns", svgns);
|
||||
svg.setAttribute("version", "1.1");
|
||||
|
||||
svg.setAttribute("style", "transform: translate(0,0)");
|
||||
}
|
||||
|
||||
createGroup(x, y, parent){
|
||||
var group = this.document.createElementNS(svgns, 'g');
|
||||
group.setAttribute("transform", "translate(" + x + ", " + y + ")");
|
||||
|
||||
parent.appendChild(group);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
setGroupOptions(group, options){
|
||||
group.setAttribute("style",
|
||||
"fill:" + options.lineColor + ";"
|
||||
);
|
||||
}
|
||||
|
||||
drawRect(x, y, width, height, parent){
|
||||
var rect = this.document.createElementNS(svgns, 'rect');
|
||||
|
||||
rect.setAttribute("x", x);
|
||||
rect.setAttribute("y", y);
|
||||
rect.setAttribute("width", width);
|
||||
rect.setAttribute("height", height);
|
||||
|
||||
parent.appendChild(rect);
|
||||
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
|
||||
export default SVGRenderer;
|
||||
@@ -0,0 +1,74 @@
|
||||
export default {
|
||||
props: {
|
||||
// 条形码内容
|
||||
value: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.value
|
||||
},
|
||||
// 条形码类型
|
||||
format: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.format
|
||||
},
|
||||
// 条形码宽度
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: () => uni.$u.props.barcode.width
|
||||
},
|
||||
// 条形码高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: () => uni.$u.props.barcode.height
|
||||
},
|
||||
// 条形码颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.color
|
||||
},
|
||||
// 背景颜色
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.backgroundColor
|
||||
},
|
||||
// 是否显示文本
|
||||
displayValue: {
|
||||
type: Boolean,
|
||||
default: () => uni.$u.props.barcode.displayValue
|
||||
},
|
||||
// 文本位置
|
||||
textPosition: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.textPosition
|
||||
},
|
||||
// 画布类型
|
||||
canvasType: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.canvasType
|
||||
},
|
||||
// 字体选项
|
||||
fontOptions: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.fontOptions
|
||||
},
|
||||
// 字体
|
||||
font: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.font
|
||||
},
|
||||
// 文本对齐方式
|
||||
textAlign: {
|
||||
type: String,
|
||||
default: () => uni.$u.props.barcode.textAlign
|
||||
},
|
||||
// 文本边距
|
||||
textMargin: {
|
||||
type: Number,
|
||||
default: () => uni.$u.props.barcode.textMargin
|
||||
},
|
||||
// 字体大小
|
||||
fontSize: {
|
||||
type: Number,
|
||||
default: () => uni.$u.props.barcode.fontSize
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<view class="u-barcode" :style="[{
|
||||
width: $u.addUnit(barcodeWidth),
|
||||
height: $u.addUnit(barcodeHeight)
|
||||
},$u.addStyle(customStyle)]">
|
||||
<canvas
|
||||
class="u-barcode__canvas"
|
||||
:type="canvasType"
|
||||
:canvas-id="canvasId"
|
||||
:id="canvasId"
|
||||
></canvas>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
import JsBarcode from './JsBarcode/JsBarcode.js';
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
|
||||
/**
|
||||
* Barcode 条形码组件
|
||||
* @description 支持生成条形码和二维码的组件,基于JsBarcode.js库
|
||||
* @tutorial https://uviewui.com/components/barcode.html
|
||||
*
|
||||
* @property {String} value 条形码内容
|
||||
* @property {String} type 条形码类型,支持code128、code39、ean13、qr等
|
||||
* @property {String|Number} width 条形码宽度
|
||||
* @property {String|Number} height 条形码高度
|
||||
* @property {String} color 条形码颜色
|
||||
* @property {String} backgroundColor 背景颜色
|
||||
* @property {Boolean} displayValue 是否显示文本
|
||||
* @property {String} textPosition 文本位置,top/bottom
|
||||
* @property {Object|String} customStyle 自定义样式
|
||||
* @property {String} fontOptions 字体选项
|
||||
* @property {String} font 字体
|
||||
* @property {String} textAlign 文本对齐方式,left/center/right
|
||||
* @property {Number} textMargin 文本边距
|
||||
* @property {Number} fontSize 字体大小
|
||||
* @example <u-barcode value="123456789" type="code128"></u-barcode>
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: "u-barcode",
|
||||
mixins: [mpMixin, mixin, props],
|
||||
data() {
|
||||
return {
|
||||
canvasId: 'barcodeId_' + uni.$u.guid(),
|
||||
ctx: null,
|
||||
barcodeWidth: null,
|
||||
barcodeHeight: null,
|
||||
textHeight: 30
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
is2d() {
|
||||
// #ifdef MP-WEIXIN
|
||||
return this.canvasType == '2d';
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
return false;
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.generateBarcode()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.generateBarcode()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 生成条形码
|
||||
async generateBarcode() {
|
||||
|
||||
const data = {};
|
||||
JsBarcode(data, this.value, {
|
||||
format: this.format,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
displayValue: false,
|
||||
});
|
||||
|
||||
// 获取编码数据
|
||||
const encodings = data.encodings[0];
|
||||
if (!encodings) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: binaryData } = encodings;
|
||||
|
||||
this.barcodeWidth = binaryData.length * this.width;
|
||||
this.barcodeHeight = this.height + (this.displayValue ? this.textHeight : 0);
|
||||
|
||||
await uni.$u.sleep(100);
|
||||
await this.initCanvas(binaryData);
|
||||
},
|
||||
|
||||
// 初始化canvas
|
||||
async initCanvas(binaryData) {
|
||||
|
||||
const query = uni
|
||||
.createSelectorQuery()
|
||||
.in(this)
|
||||
.select(`#${this.canvasId}`);
|
||||
|
||||
if (this.is2d) {
|
||||
let canvas = await new Promise((resolve) => {
|
||||
query
|
||||
.fields({
|
||||
node: true,
|
||||
size: true,
|
||||
})
|
||||
.exec((res) => {
|
||||
resolve(res[0].node);
|
||||
});
|
||||
});
|
||||
|
||||
canvas.width = this.barcodeWidth;
|
||||
canvas.height = this.barcodeHeight;
|
||||
this.ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
} else {
|
||||
// #ifdef MP-ALIPAY
|
||||
this.ctx = uni.createCanvasContext(this.canvasId);
|
||||
// #endif
|
||||
// #ifndef MP-ALIPAY
|
||||
this.ctx = uni.createCanvasContext(this.canvasId, this);
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 清空画布并设置背景色
|
||||
if (this.is2d) {
|
||||
this.ctx.fillStyle = this.backgroundColor;
|
||||
this.ctx.fillRect(0, 0, this.barcodeWidth, this.barcodeHeight);
|
||||
} else {
|
||||
this.ctx.setFillStyle(this.backgroundColor);
|
||||
this.ctx.fillRect(0, 0, this.barcodeWidth, this.barcodeHeight);
|
||||
}
|
||||
|
||||
this.drawBarcode(binaryData);
|
||||
},
|
||||
|
||||
// 绘制条形码
|
||||
drawBarcode(binaryData) {
|
||||
|
||||
const startX = 0;
|
||||
let startY = this.textPosition === 'top' ? this.textHeight : 0;
|
||||
|
||||
// 绘制条形码
|
||||
if (this.is2d) {
|
||||
this.ctx.fillStyle = this.color;
|
||||
} else {
|
||||
this.ctx.setFillStyle(this.color);
|
||||
}
|
||||
|
||||
let drawnBars = 0;
|
||||
for (let i = 0; i < binaryData.length; i++) {
|
||||
if (binaryData[i] === '1') {
|
||||
const x = startX + i * this.width;
|
||||
this.ctx.fillRect(x, startY, this.width, this.height);
|
||||
drawnBars++;
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制文本
|
||||
if (this.displayValue) {
|
||||
this.drawText();
|
||||
}
|
||||
|
||||
if (!this.is2d) {
|
||||
this.ctx.draw();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// 绘制文本
|
||||
drawText() {
|
||||
const text = this.value;
|
||||
const textColor = this.color;
|
||||
|
||||
// 构建字体字符串
|
||||
let fontString = `${this.fontSize}px ${this.font}`;
|
||||
if (this.fontOptions) {
|
||||
fontString = `${this.fontOptions} ${fontString}`;
|
||||
}
|
||||
|
||||
// 设置文本样式
|
||||
if (this.is2d) {
|
||||
this.ctx.font = fontString;
|
||||
this.ctx.fillStyle = textColor;
|
||||
this.ctx.textAlign = this.textAlign;
|
||||
this.ctx.textBaseline = 'middle';
|
||||
} else {
|
||||
this.ctx.setFontSize(this.fontSize);
|
||||
this.ctx.setFillStyle(textColor);
|
||||
this.ctx.setTextAlign(this.textAlign);
|
||||
this.ctx.setTextBaseline('middle');
|
||||
}
|
||||
|
||||
// 计算文本位置
|
||||
let textX;
|
||||
if (this.textAlign === 'left') {
|
||||
textX = this.textMargin;
|
||||
} else if (this.textAlign === 'right') {
|
||||
textX = this.barcodeWidth - this.textMargin;
|
||||
} else {
|
||||
// center
|
||||
textX = this.barcodeWidth / 2;
|
||||
}
|
||||
|
||||
let textY;
|
||||
if (this.textPosition === 'top') {
|
||||
// 文本在上方
|
||||
textY = this.fontSize / 2 + this.textMargin;
|
||||
} else {
|
||||
// 文本在下方
|
||||
textY = this.barcodeHeight - this.fontSize / 2 - this.textMargin;
|
||||
}
|
||||
|
||||
// 绘制文本
|
||||
this.ctx.fillText(text, textX, textY);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
.u-barcode {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&__canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
$u-button-active-opacity:0.75 !default;
|
||||
$u-button-loading-text-margin-left:4px !default;
|
||||
$u-button-text-color: #FFFFFF !default;
|
||||
$u-button-text-plain-error-color:$u-error !default;
|
||||
$u-button-text-plain-warning-color:$u-warning !default;
|
||||
$u-button-text-plain-success-color:$u-success !default;
|
||||
$u-button-text-plain-info-color:$u-info !default;
|
||||
$u-button-text-plain-primary-color:$u-primary !default;
|
||||
.u-button {
|
||||
&--active {
|
||||
opacity: $u-button-active-opacity;
|
||||
}
|
||||
|
||||
&--active--plain {
|
||||
background-color: rgb(217, 217, 217);
|
||||
}
|
||||
|
||||
&__loading-text {
|
||||
margin-left:$u-button-loading-text-margin-left;
|
||||
}
|
||||
|
||||
&__text,
|
||||
&__loading-text {
|
||||
color:$u-button-text-color;
|
||||
}
|
||||
|
||||
&__text--plain--error {
|
||||
color:$u-button-text-plain-error-color;
|
||||
}
|
||||
|
||||
&__text--plain--warning {
|
||||
color:$u-button-text-plain-warning-color;
|
||||
}
|
||||
|
||||
&__text--plain--success{
|
||||
color:$u-button-text-plain-success-color;
|
||||
}
|
||||
|
||||
&__text--plain--info {
|
||||
color:$u-button-text-plain-info-color;
|
||||
}
|
||||
|
||||
&__text--plain--primary {
|
||||
color:$u-button-text-plain-primary-color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* @Author : LQ
|
||||
* @Description :
|
||||
* @version : 1.0
|
||||
* @Date : 2021-08-16 10:04:04
|
||||
* @LastAuthor : LQ
|
||||
* @lastTime : 2021-08-16 10:04:24
|
||||
* @FilePath : /u-view2.0/uview-ui/components/u-button/props.js
|
||||
*/
|
||||
export default {
|
||||
props: {
|
||||
// 是否细边框
|
||||
hairline: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.button.hairline
|
||||
},
|
||||
// 按钮的预置样式,info,primary,error,warning,success
|
||||
type: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.type
|
||||
},
|
||||
// 按钮类型,text,default
|
||||
round: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.button.round
|
||||
},
|
||||
// 按钮尺寸,large,normal,small,mini
|
||||
size: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.size
|
||||
},
|
||||
// 按钮形状,circle(两边为半圆),square(带圆角)
|
||||
shape: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.shape
|
||||
},
|
||||
// 按钮是否镂空
|
||||
plain: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.button.plain
|
||||
},
|
||||
// 是否禁止状态
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.button.disabled
|
||||
},
|
||||
// 是否加载中
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.button.loading
|
||||
},
|
||||
// 加载中提示文字
|
||||
loadingText: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.button.loadingText
|
||||
},
|
||||
// 加载状态图标类型
|
||||
loadingMode: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.loadingMode
|
||||
},
|
||||
// 加载图标大小
|
||||
loadingSize: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.button.loadingSize
|
||||
},
|
||||
// 开放能力,具体请看uniapp稳定关于button组件部分说明
|
||||
// https://uniapp.dcloud.io/component/button
|
||||
openType: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.openType
|
||||
},
|
||||
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
|
||||
// 取值为submit(提交表单),reset(重置表单)
|
||||
formType: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.formType
|
||||
},
|
||||
// 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
|
||||
// 只微信小程序、QQ小程序有效
|
||||
appParameter: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.appParameter
|
||||
},
|
||||
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
|
||||
hoverStopPropagation: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.button.hoverStopPropagation
|
||||
},
|
||||
// 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
|
||||
lang: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.lang
|
||||
},
|
||||
// 会话来源,open-type="contact"时有效。只微信小程序有效
|
||||
sessionFrom: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.sessionFrom
|
||||
},
|
||||
// 会话内消息卡片标题,open-type="contact"时有效
|
||||
// 默认当前标题,只微信小程序有效
|
||||
sendMessageTitle: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.sendMessageTitle
|
||||
},
|
||||
// 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
|
||||
// 默认当前分享路径,只微信小程序有效
|
||||
sendMessagePath: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.sendMessagePath
|
||||
},
|
||||
// 会话内消息卡片图片,open-type="contact"时有效
|
||||
// 默认当前页面截图,只微信小程序有效
|
||||
sendMessageImg: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.sendMessageImg
|
||||
},
|
||||
// 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
|
||||
// 用户点击后可以快速发送小程序消息,open-type="contact"时有效
|
||||
showMessageCard: {
|
||||
type: Boolean,
|
||||
default: ()=> uni.$u.props.button.showMessageCard
|
||||
},
|
||||
// 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
|
||||
dataName: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.dataName
|
||||
},
|
||||
// 节流,一定时间内只能触发一次
|
||||
throttleTime: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.button.throttleTime
|
||||
},
|
||||
// 按住后多久出现点击态,单位毫秒
|
||||
hoverStartTime: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.button.hoverStartTime
|
||||
},
|
||||
// 手指松开后点击态保留时间,单位毫秒
|
||||
hoverStayTime: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.button.hoverStayTime
|
||||
},
|
||||
// 按钮文字,之所以通过props传入,是因为slot传入的话
|
||||
// nvue中无法控制文字的样式
|
||||
text: {
|
||||
type: [String, Number],
|
||||
default: ()=> uni.$u.props.button.text
|
||||
},
|
||||
// 按钮图标
|
||||
icon: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.icon
|
||||
},
|
||||
// 按钮图标
|
||||
iconColor: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.iconColor
|
||||
},
|
||||
// 按钮图标位置:left, right
|
||||
iconPosition: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.iconPosition
|
||||
},
|
||||
// 按钮颜色,支持传入linear-gradient渐变色
|
||||
color: {
|
||||
type: String,
|
||||
default: ()=> uni.$u.props.button.color
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,497 @@
|
||||
<template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<button
|
||||
class="u-button u-reset-button"
|
||||
:hover-class="!disabled && !loading ? 'u-button--active' : ''"
|
||||
:style="[baseColor, $u.addStyle(customStyle)]"
|
||||
:class="bemClass"
|
||||
:hover-start-time="Number(hoverStartTime)"
|
||||
:hover-stay-time="Number(hoverStayTime)"
|
||||
:form-type="formType"
|
||||
:open-type="openType"
|
||||
:app-parameter="appParameter"
|
||||
:hover-stop-propagation="hoverStopPropagation"
|
||||
:send-message-title="sendMessageTitle"
|
||||
:send-message-path="sendMessagePath"
|
||||
:lang="lang"
|
||||
:data-name="dataName"
|
||||
:session-from="sessionFrom"
|
||||
:send-message-img="sendMessageImg"
|
||||
:show-message-card="showMessageCard"
|
||||
@getphonenumber="getphonenumber"
|
||||
@getuserinfo="getuserinfo"
|
||||
@error="error"
|
||||
@opensetting="opensetting"
|
||||
@launchapp="launchapp"
|
||||
@agreeprivacyauthorization="agreeprivacyauthorization"
|
||||
@chooseavatar="chooseavatar"
|
||||
@tap="clickHandler"
|
||||
>
|
||||
<template v-if="loading">
|
||||
<u-loading-icon :mode="loadingMode" :size="loadingSize * 1.15" :color="elLoadingColor"></u-loading-icon>
|
||||
<text class="u-button__loading-text" :style="[textStyle]">{{ loadingText || text}}</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<u-icon v-if="icon" :name="icon" :color="elIconColor" :size="textSize * 1.35"></u-icon>
|
||||
<slot>
|
||||
<text class="u-button__text" :style="[textStyle]">{{ text }}</text>
|
||||
</slot>
|
||||
</template>
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<view
|
||||
class="u-button"
|
||||
:class="bemClass"
|
||||
:style="[baseColor, $u.addStyle(customStyle)]"
|
||||
:hover-start-time="Number(hoverStartTime)"
|
||||
:hover-stay-time="Number(hoverStayTime)"
|
||||
:hover-class="!disabled && !loading && !color && (plain || type === 'info')
|
||||
? 'u-button--active--plain'
|
||||
: !disabled && !loading && !plain
|
||||
? 'u-button--active'
|
||||
: ''
|
||||
"
|
||||
@tap="clickHandler"
|
||||
>
|
||||
<template v-if="loading">
|
||||
<u-loading-icon :mode="loadingMode" :size="loadingSize * 1.15" :color="elLoadingColor"></u-loading-icon>
|
||||
<text class="u-button__loading-text" :style="[textStyle]"
|
||||
:class="[plain && `u-button__text--plain--${type}`]">{{ loadingText || text }}</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<u-icon v-if="icon" :name="icon" :color="elIconColor" :size="textSize * 1.35"></u-icon>
|
||||
<text class="u-button__text" :class="[plain && `u-button__text--plain--${type}`]"
|
||||
:style="[textStyle]">{{ text }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import button from "../../libs/mixin/button.js"
|
||||
import openType from "../../libs/mixin/openType.js"
|
||||
import props from "./props.js"
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin'
|
||||
|
||||
/**
|
||||
* button 按钮
|
||||
* @description Button 按钮
|
||||
* @tutorial https://www.uviewui.com/components/button.html
|
||||
*
|
||||
* @property {Boolean} hairline 是否显示按钮的细边框 (默认 true )
|
||||
* @property {String} type 按钮的预置样式,info,primary,error,warning,success (默认 'info' )
|
||||
* @property {String} size 按钮尺寸,large,normal,mini (默认 normal)
|
||||
* @property {String} shape 按钮形状,circle(两边为半圆),square(带圆角) (默认 'square' )
|
||||
* @property {Boolean} plain 按钮是否镂空,背景色透明 (默认 false)
|
||||
* @property {Boolean} disabled 是否禁用 (默认 false)
|
||||
* @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈) (默认 false)
|
||||
* @property {String | Number} loadingText 加载中提示文字
|
||||
* @property {String} loadingMode 加载状态图标类型 (默认 'spinner' )
|
||||
* @property {String | Number} loadingSize 加载图标大小 (默认 15 )
|
||||
* @property {String} openType 开放能力,具体请看uniapp稳定关于button组件部分说明
|
||||
* @property {String} formType 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
|
||||
* @property {String} appParameter 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 (注:只微信小程序、QQ小程序有效)
|
||||
* @property {Boolean} hoverStopPropagation 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true )
|
||||
* @property {String} lang 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文(默认 en )
|
||||
* @property {String} sessionFrom 会话来源,openType="contact"时有效
|
||||
* @property {String} sendMessageTitle 会话内消息卡片标题,openType="contact"时有效
|
||||
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径,openType="contact"时有效
|
||||
* @property {String} sendMessageImg 会话内消息卡片图片,openType="contact"时有效
|
||||
* @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,openType="contact"时有效(默认false)
|
||||
* @property {String} dataName 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
|
||||
* @property {String | Number} throttleTime 节流,一定时间内只能触发一次 (默认 0 )
|
||||
* @property {String | Number} hoverStartTime 按住后多久出现点击态,单位毫秒 (默认 0 )
|
||||
* @property {String | Number} hoverStayTime 手指松开后点击态保留时间,单位毫秒 (默认 200 )
|
||||
* @property {String | Number} text 按钮文字,之所以通过props传入,是因为slot传入的话(注:nvue中无法控制文字的样式)
|
||||
* @property {String} icon 按钮图标
|
||||
* @property {String} iconColor 按钮图标颜色
|
||||
* @property {String} color 按钮颜色,支持传入linear-gradient渐变色
|
||||
* @property {Object} customStyle 定义需要用到的外部样式
|
||||
*
|
||||
* @event {Function} click 非禁止并且非加载中,才能点击
|
||||
* @event {Function} getphonenumber open-type="getPhoneNumber"时有效
|
||||
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
|
||||
* @event {Function} error 当使用开放能力时,发生错误的回调
|
||||
* @event {Function} opensetting 在打开授权设置页并关闭后回调
|
||||
* @event {Function} launchapp 打开 APP 成功的回调
|
||||
* @event {Function} chooseavatar 选择头像回调,openType="chooseAvatar"时有效
|
||||
* @event {Function} agreeprivacyauthorization 用户同意隐私协议事件回调
|
||||
* @example <u-button>月落</u-button>
|
||||
*/
|
||||
export default {
|
||||
name: "u-button",
|
||||
mixins: [
|
||||
mpMixin,
|
||||
mixin,
|
||||
props,
|
||||
// #ifdef MP
|
||||
button,
|
||||
openType
|
||||
// #endif
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
elLoadingColor:'',
|
||||
elIconColor:''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 生成bem风格的类名
|
||||
bemClass() {
|
||||
// this.bem为一个computed变量,在mixin中
|
||||
if (!this.color) {
|
||||
return this.bem(
|
||||
"button",
|
||||
["type", "shape", "size"],
|
||||
["disabled", "plain", "hairline"]
|
||||
);
|
||||
} else {
|
||||
// 由于nvue的原因,在有color参数时,不需要传入type,否则会生成type相关的类型,影响最终的样式
|
||||
return this.bem(
|
||||
"button",
|
||||
["shape", "size"],
|
||||
["disabled", "plain", "hairline"]
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
baseColor() {
|
||||
let style = {};
|
||||
if (this.color) {
|
||||
// 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
|
||||
style.color = this.plain ? this.color : "white";
|
||||
if (!this.plain) {
|
||||
// 非镂空,背景色使用自定义的颜色
|
||||
style["background-color"] = this.color;
|
||||
}
|
||||
if (this.color.indexOf("gradient") !== -1) {
|
||||
// 如果自定义的颜色为渐变色,不显示边框,以及通过backgroundImage设置渐变色
|
||||
// weex文档说明可以写borderWidth的形式,为什么这里需要分开写?
|
||||
// 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西,所以需要这么写才有效
|
||||
style.borderTopWidth = 0;
|
||||
style.borderRightWidth = 0;
|
||||
style.borderBottomWidth = 0;
|
||||
style.borderLeftWidth = 0;
|
||||
if (!this.plain) {
|
||||
style.backgroundImage = this.color;
|
||||
}
|
||||
} else {
|
||||
// 非渐变色,则设置边框相关的属性
|
||||
style.borderColor = this.color;
|
||||
style.borderWidth = "1px";
|
||||
style.borderStyle = "solid";
|
||||
}
|
||||
}
|
||||
|
||||
// 为了兼容安卓nvue,只能这么分开写
|
||||
let radius = this.shape == 'circle' ? 100 : this.round
|
||||
if(radius) {
|
||||
radius = uni.$u.addUnit(radius)
|
||||
style.borderTopLeftRadius = radius
|
||||
style.borderTopRightRadius = radius
|
||||
style.borderBottomLeftRadius = radius
|
||||
style.borderBottomRightRadius = radius
|
||||
}
|
||||
|
||||
if(this.icon && this.iconPosition === 'right'){
|
||||
style.flexDirection = 'row-reverse';
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
// nvue版本按钮的字体不会继承父组件的颜色,需要对每一个text组件进行单独的设置
|
||||
textStyle() {
|
||||
let style = {};
|
||||
style.fontSize = uni.$u.addUnit(this.textSize);
|
||||
|
||||
if(this.icon){
|
||||
if(this.iconPosition === 'left'){
|
||||
style.marginLeft = uni.$u.addUnit(2);
|
||||
}else{
|
||||
style.marginRight = uni.$u.addUnit(2);
|
||||
}
|
||||
}
|
||||
|
||||
//#ifdef APP-NVUE
|
||||
// 针对自定义了color颜色的情况,镂空状态下,就是用自定义的颜色
|
||||
if (this.type === "info") {
|
||||
style.color = this.$u.theme.mainColor;
|
||||
}
|
||||
if (this.color) {
|
||||
style.color = this.plain ? this.color : "white";
|
||||
}
|
||||
// #endif
|
||||
|
||||
return style;
|
||||
},
|
||||
// 字体大小
|
||||
textSize() {
|
||||
let fontSize = 14,
|
||||
{ size } = this;
|
||||
if (size === "large") fontSize = 16;
|
||||
if (size === "normal") fontSize = 14;
|
||||
if (size === "small") fontSize = 12;
|
||||
if (size === "mini") fontSize = 10;
|
||||
return fontSize;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
color: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.iconColorCom()
|
||||
this.loadingColorCom()
|
||||
}
|
||||
},
|
||||
iconColor: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
this.iconColorCom()
|
||||
}
|
||||
}
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ['click', 'error', 'launchapp', 'opensetting', 'getuserinfo', 'getphonenumber', 'agreeprivacyauthorization', 'chooseavatar'],
|
||||
// #endif
|
||||
methods: {
|
||||
iconColorCom() {
|
||||
// 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
|
||||
// u-icon的color能接受一个主题颜色的值
|
||||
if (this.iconColor) this.elIconColor = this.iconColor;
|
||||
if (this.plain) {
|
||||
this.elIconColor = this.color ? this.color : this.type;
|
||||
} else {
|
||||
this.elIconColor = this.type === "info" ? "#000000" : "#ffffff";
|
||||
}
|
||||
},
|
||||
loadingColorCom() {
|
||||
if (this.plain) {
|
||||
// 如果有设置color值,则用color值,否则使用type主题颜色
|
||||
this.elLoadingColor = this.color ? this.color : this.$u.theme[this.type];
|
||||
}
|
||||
if (this.type === "info") {
|
||||
this.elLoadingColor = "#c9c9c9";
|
||||
}
|
||||
this.elLoadingColor = "rgb(200, 200, 200)";
|
||||
},
|
||||
clickHandler(e) {
|
||||
// 非禁止并且非加载中,才能点击
|
||||
if (!this.disabled && !this.loading) {
|
||||
// 进行节流控制,每this.throttle毫秒内,只在开始处执行
|
||||
uni.$u.throttle(() => {
|
||||
this.$emit("click", e);
|
||||
}, this.throttleTime);
|
||||
}
|
||||
},
|
||||
// 下面为对接uniapp官方按钮开放能力事件回调的对接
|
||||
getphonenumber(res) {
|
||||
this.$emit("getphonenumber", res);
|
||||
},
|
||||
getuserinfo(res) {
|
||||
this.$emit("getuserinfo", res);
|
||||
},
|
||||
error(res) {
|
||||
this.$emit("error", res);
|
||||
},
|
||||
opensetting(res) {
|
||||
this.$emit("opensetting", res);
|
||||
},
|
||||
launchapp(res) {
|
||||
this.$emit("launchapp", res);
|
||||
},
|
||||
agreeprivacyauthorization(res) {
|
||||
this.$emit("agreeprivacyauthorization", res);
|
||||
},
|
||||
chooseavatar(res) {
|
||||
this.$emit("chooseavatar", res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
@import "./vue.scss";
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef APP-NVUE */
|
||||
@import "./nvue.scss";
|
||||
/* #endif */
|
||||
|
||||
$u-button-u-button-height: 40px !default;
|
||||
$u-button-text-font-size: 15px !default;
|
||||
$u-button-loading-text-font-size: 15px !default;
|
||||
$u-button-loading-text-margin-left: 4px !default;
|
||||
$u-button-large-width: 100% !default;
|
||||
$u-button-large-height: 50px !default;
|
||||
$u-button-normal-padding: 0 12px !default;
|
||||
$u-button-large-padding: 0 15px !default;
|
||||
$u-button-normal-font-size: 14px !default;
|
||||
$u-button-small-min-width: 60px !default;
|
||||
$u-button-small-height: 30px !default;
|
||||
$u-button-small-padding: 0px 8px !default;
|
||||
$u-button-mini-padding: 0px 8px !default;
|
||||
$u-button-small-font-size: 12px !default;
|
||||
$u-button-mini-height: 22px !default;
|
||||
$u-button-mini-font-size: 10px !default;
|
||||
$u-button-mini-min-width: 50px !default;
|
||||
$u-button-disabled-opacity: 0.5 !default;
|
||||
$u-button-info-color: #323233 !default;
|
||||
$u-button-info-background-color: $u-info-light !default;
|
||||
$u-button-info-border-color: $u-info-light !default;
|
||||
$u-button-info-border-width: 1px !default;
|
||||
$u-button-info-border-style: solid !default;
|
||||
$u-button-success-color: #fff !default;
|
||||
$u-button-success-background-color: $u-success !default;
|
||||
$u-button-success-border-color: $u-button-success-background-color !default;
|
||||
$u-button-success-border-width: 1px !default;
|
||||
$u-button-success-border-style: solid !default;
|
||||
$u-button-primary-color: #fff !default;
|
||||
$u-button-primary-background-color: $u-primary !default;
|
||||
$u-button-primary-border-color: $u-button-primary-background-color !default;
|
||||
$u-button-primary-border-width: 1px !default;
|
||||
$u-button-primary-border-style: solid !default;
|
||||
$u-button-error-color: #fff !default;
|
||||
$u-button-error-background-color: $u-error !default;
|
||||
$u-button-error-border-color: $u-button-error-background-color !default;
|
||||
$u-button-error-border-width: 1px !default;
|
||||
$u-button-error-border-style: solid !default;
|
||||
$u-button-warning-color: #fff !default;
|
||||
$u-button-warning-background-color: $u-warning !default;
|
||||
$u-button-warning-border-color: $u-button-warning-background-color !default;
|
||||
$u-button-warning-border-width: 1px !default;
|
||||
$u-button-warning-border-style: solid !default;
|
||||
$u-button-block-width: 100% !default;
|
||||
$u-button-icon-min-width: 1em !default;
|
||||
$u-button-plain-background-color: #fff !default;
|
||||
$u-button-hairline-border-width: 0.5px !default;
|
||||
|
||||
.u-button {
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include flex;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
|
||||
&__text {
|
||||
font-size: $u-button-text-font-size;
|
||||
}
|
||||
|
||||
&__loading-text {
|
||||
font-size: $u-button-loading-text-font-size;
|
||||
margin-left: $u-button-loading-text-margin-left;
|
||||
}
|
||||
|
||||
&--large {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: $u-button-large-width;
|
||||
/* #endif */
|
||||
height: $u-button-large-height;
|
||||
padding: $u-button-large-padding;
|
||||
}
|
||||
|
||||
&--normal {
|
||||
height: $u-button-u-button-height;
|
||||
padding: $u-button-normal-padding;
|
||||
font-size: $u-button-normal-font-size;
|
||||
}
|
||||
|
||||
&--small {
|
||||
/* #ifndef APP-NVUE */
|
||||
min-width: $u-button-small-min-width;
|
||||
/* #endif */
|
||||
height: $u-button-small-height;
|
||||
padding: $u-button-small-padding;
|
||||
font-size: $u-button-small-font-size;
|
||||
}
|
||||
|
||||
&--mini {
|
||||
height: $u-button-mini-height;
|
||||
font-size: $u-button-mini-font-size;
|
||||
/* #ifndef APP-NVUE */
|
||||
min-width: $u-button-mini-min-width;
|
||||
/* #endif */
|
||||
padding: $u-button-mini-padding;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
opacity: $u-button-disabled-opacity;
|
||||
}
|
||||
|
||||
&--info {
|
||||
color: $u-button-info-color;
|
||||
background-color: $u-button-info-background-color;
|
||||
border-color: $u-button-info-border-color;
|
||||
border-width: $u-button-info-border-width;
|
||||
border-style: $u-button-info-border-style;
|
||||
}
|
||||
|
||||
&--success {
|
||||
color: $u-button-success-color;
|
||||
background-color: $u-button-success-background-color;
|
||||
border-color: $u-button-success-border-color;
|
||||
border-width: $u-button-success-border-width;
|
||||
border-style: $u-button-success-border-style;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
color: $u-button-primary-color;
|
||||
background-color: $u-button-primary-background-color;
|
||||
border-color: $u-button-primary-border-color;
|
||||
border-width: $u-button-primary-border-width;
|
||||
border-style: $u-button-primary-border-style;
|
||||
}
|
||||
|
||||
&--error {
|
||||
color: $u-button-error-color;
|
||||
background-color: $u-button-error-background-color;
|
||||
border-color: $u-button-error-border-color;
|
||||
border-width: $u-button-error-border-width;
|
||||
border-style: $u-button-error-border-style;
|
||||
}
|
||||
|
||||
&--warning {
|
||||
color: $u-button-warning-color;
|
||||
background-color: $u-button-warning-background-color;
|
||||
border-color: $u-button-warning-border-color;
|
||||
border-width: $u-button-warning-border-width;
|
||||
border-style: $u-button-warning-border-style;
|
||||
}
|
||||
|
||||
&--block {
|
||||
@include flex;
|
||||
width: $u-button-block-width;
|
||||
}
|
||||
|
||||
&--circle {
|
||||
|
||||
}
|
||||
|
||||
&--square {
|
||||
|
||||
}
|
||||
|
||||
&__icon {
|
||||
/* #ifndef APP-NVUE */
|
||||
min-width: $u-button-icon-min-width;
|
||||
line-height: inherit !important;
|
||||
vertical-align: top;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
&--plain {
|
||||
background-color: $u-button-plain-background-color;
|
||||
}
|
||||
|
||||
&--hairline {
|
||||
border-width: $u-button-hairline-border-width !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,80 @@
|
||||
// nvue下hover-class无效
|
||||
$u-button-before-top:50% !default;
|
||||
$u-button-before-left:50% !default;
|
||||
$u-button-before-width:100% !default;
|
||||
$u-button-before-height:100% !default;
|
||||
$u-button-before-transform:translate(-50%, -50%) !default;
|
||||
$u-button-before-opacity:0 !default;
|
||||
$u-button-before-background-color:#000 !default;
|
||||
$u-button-before-border-color:#000 !default;
|
||||
$u-button-active-before-opacity:.15 !default;
|
||||
$u-button-icon-margin-left:4px !default;
|
||||
$u-button-plain-u-button-info-color:$u-info;
|
||||
$u-button-plain-u-button-success-color:$u-success;
|
||||
$u-button-plain-u-button-error-color:$u-error;
|
||||
$u-button-plain-u-button-warning-color:$u-error;
|
||||
|
||||
.u-button {
|
||||
width: 100%;
|
||||
|
||||
&__text {
|
||||
white-space: nowrap;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top:$u-button-before-top;
|
||||
left:$u-button-before-left;
|
||||
width:$u-button-before-width;
|
||||
height:$u-button-before-height;
|
||||
border: inherit;
|
||||
border-radius: inherit;
|
||||
transform:$u-button-before-transform;
|
||||
opacity:$u-button-before-opacity;
|
||||
content: " ";
|
||||
background-color:$u-button-before-background-color;
|
||||
border-color:$u-button-before-border-color;
|
||||
}
|
||||
|
||||
&--active {
|
||||
&:before {
|
||||
opacity: .15
|
||||
}
|
||||
}
|
||||
|
||||
&__icon+&__text:not(:empty),
|
||||
&__loading-text {
|
||||
margin-left:$u-button-icon-margin-left;
|
||||
}
|
||||
|
||||
&--plain {
|
||||
&.u-button--primary {
|
||||
color: $u-primary;
|
||||
}
|
||||
}
|
||||
|
||||
&--plain {
|
||||
&.u-button--info {
|
||||
color:$u-button-plain-u-button-info-color;
|
||||
}
|
||||
}
|
||||
|
||||
&--plain {
|
||||
&.u-button--success {
|
||||
color:$u-button-plain-u-button-success-color;
|
||||
}
|
||||
}
|
||||
|
||||
&--plain {
|
||||
&.u-button--error {
|
||||
color:$u-button-plain-u-button-error-color;
|
||||
}
|
||||
}
|
||||
|
||||
&--plain {
|
||||
&.u-button--warning {
|
||||
color:$u-button-plain-u-button-warning-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<view class="u-calendar-header u-border-bottom">
|
||||
<text class="u-calendar-header__title" v-if="showTitle">
|
||||
{{ title }}
|
||||
</text>
|
||||
<text class="u-calendar-header__subtitle" v-if="showSubtitle">
|
||||
{{ subtitle }}
|
||||
</text>
|
||||
<view class="u-calendar-header__weekdays">
|
||||
<text
|
||||
v-for="(item, index) in weekdaysList"
|
||||
:key="index"
|
||||
class="u-calendar-header__weekdays__weekday"
|
||||
>
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import mixin from '../../libs/mixin/mixin';
|
||||
import mpMixin from '../../libs/mixin/mpMixin';
|
||||
export default {
|
||||
name: 'u-calendar-header',
|
||||
mixins: [mpMixin, mixin],
|
||||
props: {
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 副标题
|
||||
subtitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 是否显示标题
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 是否显示副标题
|
||||
showSubtitle: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 星期几
|
||||
weekdays: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
weekdaysList() {
|
||||
return this.weekdays.split(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
name() {},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../libs/css/components.scss';
|
||||
|
||||
.u-calendar-header {
|
||||
padding-bottom: 4px;
|
||||
|
||||
&__title {
|
||||
font-size: 16px;
|
||||
color: $u-main-color;
|
||||
text-align: center;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-size: 14px;
|
||||
color: $u-main-color;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__weekdays {
|
||||
@include flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&__weekday {
|
||||
font-size: 13px;
|
||||
color: $u-main-color;
|
||||
line-height: 30px;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,591 @@
|
||||
<template>
|
||||
<view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper">
|
||||
<view v-for="(item, index) in months" :key="index" :class="[`u-calendar-month-${index}`]"
|
||||
:ref="`u-calendar-month-${index}`" :id="`month-${index}`">
|
||||
<text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}年{{ item.month }}月</text>
|
||||
<view class="u-calendar-month__days">
|
||||
<view v-if="showMark" class="u-calendar-month__days__month-mark-wrapper">
|
||||
<text class="u-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text>
|
||||
</view>
|
||||
<view class="u-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1"
|
||||
:style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)"
|
||||
:class="[item1.selected && 'u-calendar-month__days__day__select--selected']">
|
||||
<view class="u-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]">
|
||||
<text class="u-calendar-month__days__day__select__info"
|
||||
:class="[item1.disabled && 'u-calendar-month__days__day__select__info--disabled']"
|
||||
:style="[textStyle(item1)]">{{ item1.day }}</text>
|
||||
<text v-if="getBottomInfo(index, index1, item1)"
|
||||
class="u-calendar-month__days__day__select__buttom-info"
|
||||
:class="[item1.disabled && 'u-calendar-month__days__day__select__buttom-info--disabled']"
|
||||
:style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text>
|
||||
<text v-if="item1.dot" class="u-calendar-month__days__day__select__dot"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import mixin from '../../libs/mixin/mixin'
|
||||
import mpMixin from '../../libs/mixin/mpMixin'
|
||||
// #ifdef APP-NVUE
|
||||
// 由于nvue不支持百分比单位,需要查询宽度来计算每个日期的宽度
|
||||
const dom = uni.requireNativePlugin('dom')
|
||||
// #endif
|
||||
import dayjs from '../../libs/util/dayjs.js';
|
||||
export default {
|
||||
name: 'u-calendar-month',
|
||||
mixins: [mpMixin, mixin],
|
||||
props: {
|
||||
// 是否显示月份背景色
|
||||
showMark: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 主题色,对底部按钮和选中日期有效
|
||||
color: {
|
||||
type: String,
|
||||
default: '#3c9cff'
|
||||
},
|
||||
// 月份数据
|
||||
months: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 日期选择类型
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'single'
|
||||
},
|
||||
// 日期行高
|
||||
rowHeight: {
|
||||
type: [String, Number],
|
||||
default: 58
|
||||
},
|
||||
// mode=multiple时,最多可选多少个日期
|
||||
maxCount: {
|
||||
type: [String, Number],
|
||||
default: Infinity
|
||||
},
|
||||
// mode=range时,第一个日期底部的提示文字
|
||||
startText: {
|
||||
type: String,
|
||||
default: '开始'
|
||||
},
|
||||
// mode=range时,最后一个日期底部的提示文字
|
||||
endText: {
|
||||
type: String,
|
||||
default: '结束'
|
||||
},
|
||||
// 默认选中的日期,mode为multiple或range是必须为数组格式
|
||||
defaultDate: {
|
||||
type: [Array, String, Date],
|
||||
default: null
|
||||
},
|
||||
// 最小的可选日期
|
||||
minDate: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 最大可选日期
|
||||
maxDate: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 如果没有设置maxDate,则往后推多少个月
|
||||
maxMonth: {
|
||||
type: [String, Number],
|
||||
default: 2
|
||||
},
|
||||
// 是否为只读状态,只读状态下禁止选择日期
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 日期区间最多可选天数,默认无限制,mode = range时有效
|
||||
maxRange: {
|
||||
type: [Number, String],
|
||||
default: Infinity
|
||||
},
|
||||
// 范围选择超过最多可选天数时的提示文案,mode = range时有效
|
||||
rangePrompt: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效
|
||||
showRangePrompt: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否允许日期范围的起止时间为同一天,mode = range时有效
|
||||
allowSameDay: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 选中的日期的形状,circle(圆形),square(带圆角) (默认 'square' )
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'square'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 每个日期的宽度
|
||||
width: 0,
|
||||
// 当前选中的日期item
|
||||
item: {},
|
||||
selected: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedChange: {
|
||||
immediate: true,
|
||||
handler(n) {
|
||||
this.setDefaultDate()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 多个条件的变化,会引起选中日期的变化,这里统一管理监听
|
||||
selectedChange() {
|
||||
return [this.minDate, this.maxDate, this.defaultDate]
|
||||
},
|
||||
dayStyle(index1, index2, item) {
|
||||
return (index1, index2, item) => {
|
||||
const style = {}
|
||||
let week = item.week
|
||||
// 不进行四舍五入的形式保留2位小数
|
||||
const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1))
|
||||
// 得出每个日期的宽度
|
||||
// #ifdef APP-NVUE
|
||||
style.width = uni.$u.addUnit(dayWidth)
|
||||
// #endif
|
||||
style.height = uni.$u.addUnit(this.rowHeight)
|
||||
if (index2 === 0) {
|
||||
// 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数
|
||||
week = (week === 0 ? 7 : week) - 1
|
||||
style.marginLeft = uni.$u.addUnit(week * dayWidth)
|
||||
}
|
||||
if (this.mode === 'range') {
|
||||
// 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
|
||||
style.paddingLeft = 0
|
||||
style.paddingRight = 0
|
||||
style.paddingBottom = 0
|
||||
style.paddingTop = 0
|
||||
}
|
||||
return style
|
||||
}
|
||||
},
|
||||
daySelectStyle() {
|
||||
return (index1, index2, item) => {
|
||||
let date = dayjs(item.date).format("YYYY-MM-DD"),
|
||||
style = {}
|
||||
// 判断date是否在selected数组中,因为月份可能会需要补0,所以使用dateSame判断,而不用数组的includes判断
|
||||
if (this.selected.some(item => this.dateSame(item, date))) {
|
||||
style.backgroundColor = this.color
|
||||
}
|
||||
let radius = this.shape === 'circle' ? '100%' : '5px'
|
||||
if (this.mode === 'single') {
|
||||
if (date === this.selected[0]) {
|
||||
// 因为需要对nvue的兼容,只能这么写,无法缩写,也无法通过类名控制等等
|
||||
|
||||
style.borderTopLeftRadius = radius
|
||||
style.borderBottomLeftRadius = radius
|
||||
style.borderTopRightRadius = radius
|
||||
style.borderBottomRightRadius = radius
|
||||
}
|
||||
} else if (this.mode === 'range') {
|
||||
if (this.selected.length >= 2) {
|
||||
const len = this.selected.length - 1
|
||||
// 第一个日期设置左上角和左下角的圆角
|
||||
if (this.dateSame(date, this.selected[0])) {
|
||||
style.borderTopLeftRadius = radius
|
||||
style.borderBottomLeftRadius = radius
|
||||
}
|
||||
// 最后一个日期设置右上角和右下角的圆角
|
||||
if (this.dateSame(date, this.selected[len])) {
|
||||
style.borderTopRightRadius = radius
|
||||
style.borderBottomRightRadius = radius
|
||||
}
|
||||
// 处于第一和最后一个之间的日期,背景色设置为浅色,通过将对应颜色进行等分,再取其尾部的颜色值
|
||||
if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
|
||||
.selected[len]))) {
|
||||
style.backgroundColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[90]
|
||||
// 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符
|
||||
style.opacity = 0.7
|
||||
}
|
||||
} else if (this.selected.length === 1) {
|
||||
// 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
|
||||
// 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现
|
||||
style.borderTopLeftRadius = radius
|
||||
style.borderBottomLeftRadius = radius
|
||||
}
|
||||
} else {
|
||||
if (this.selected.some(item => this.dateSame(item, date))) {
|
||||
style.borderTopLeftRadius = radius
|
||||
style.borderBottomLeftRadius = radius
|
||||
style.borderTopRightRadius = radius
|
||||
style.borderBottomRightRadius = radius
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
},
|
||||
// 某个日期是否被选中
|
||||
textStyle() {
|
||||
return (item) => {
|
||||
const date = dayjs(item.date).format("YYYY-MM-DD"),
|
||||
style = {}
|
||||
// 选中的日期,提示文字设置白色
|
||||
if (this.selected.some(item => this.dateSame(item, date))) {
|
||||
style.color = '#ffffff'
|
||||
}
|
||||
if (this.mode === 'range') {
|
||||
const len = this.selected.length - 1
|
||||
// 如果是范围选择模式,第一个和最后一个之间的日期,文字颜色设置为高亮的主题色
|
||||
if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
|
||||
.selected[len]))) {
|
||||
style.color = this.color
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
},
|
||||
// 获取底部的提示文字
|
||||
getBottomInfo() {
|
||||
return (index1, index2, item) => {
|
||||
const date = dayjs(item.date).format("YYYY-MM-DD")
|
||||
const bottomInfo = item.bottomInfo
|
||||
// 当为日期范围模式时,且选择的日期个数大于0时
|
||||
if (this.mode === 'range' && this.selected.length > 0) {
|
||||
if (this.selected.length === 1) {
|
||||
// 选择了一个日期时,如果当前日期为数组中的第一个日期,则显示底部文字为“开始”
|
||||
if (this.dateSame(date, this.selected[0])) return this.startText
|
||||
else return bottomInfo
|
||||
} else {
|
||||
const len = this.selected.length - 1
|
||||
// 如果数组中的日期大于2个时,第一个和最后一个显示为开始和结束日期
|
||||
if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) &&
|
||||
len === 1) {
|
||||
// 如果长度为2,且第一个等于第二个日期,则提示语放在同一个item中
|
||||
return `${this.startText}/${this.endText}`
|
||||
} else if (this.dateSame(date, this.selected[0])) {
|
||||
return this.startText
|
||||
} else if (this.dateSame(date, this.selected[len])) {
|
||||
return this.endText
|
||||
} else {
|
||||
return bottomInfo
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return bottomInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
// #ifdef VUE3
|
||||
emits: ["monthSelected", "updateMonthTop"],
|
||||
// #endif
|
||||
methods: {
|
||||
init() {
|
||||
// 初始化默认选中
|
||||
this.$emit('monthSelected', this.selected)
|
||||
this.$nextTick(() => {
|
||||
// 这里需要另一个延时,因为获取宽度后,会进行月份数据渲染,只有渲染完成之后,才有真正的高度
|
||||
// 因为nvue下,$nextTick并不是100%可靠的
|
||||
uni.$u.sleep(10).then(() => {
|
||||
this.getWrapperWidth()
|
||||
this.getMonthRect()
|
||||
})
|
||||
})
|
||||
},
|
||||
// 判断两个日期是否相等
|
||||
dateSame(date1, date2) {
|
||||
return dayjs(date1).isSame(dayjs(date2))
|
||||
},
|
||||
// 获取月份数据区域的宽度,因为nvue不支持百分比,所以无法通过css设置每个日期item的宽度
|
||||
getWrapperWidth() {
|
||||
// #ifdef APP-NVUE
|
||||
dom.getComponentRect(this.$refs['u-calendar-month-wrapper'], res => {
|
||||
this.width = res.size.width
|
||||
})
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
this.$uGetRect('.u-calendar-month-wrapper').then(size => {
|
||||
this.width = size.width
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
getMonthRect() {
|
||||
// 获取每个月份数据的尺寸,用于父组件在scroll-view滚动事件中,监听当前滚动到了第几个月份
|
||||
const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise(
|
||||
`u-calendar-month-${index}`))
|
||||
// 一次性返回
|
||||
Promise.all(promiseAllArr).then(
|
||||
sizes => {
|
||||
let height = 1
|
||||
const topArr = []
|
||||
for (let i = 0; i < this.months.length; i++) {
|
||||
// 添加到months数组中,供scroll-view滚动事件中,判断当前滚动到哪个月份
|
||||
topArr[i] = height
|
||||
height += sizes[i].height
|
||||
}
|
||||
// 由于微信下,无法通过this.months[i].top的形式(引用类型)去修改父组件的month的top值,所以使用事件形式对外发出
|
||||
this.$emit('updateMonthTop', topArr)
|
||||
})
|
||||
},
|
||||
// 获取每个月份区域的尺寸
|
||||
getMonthRectByPromise(el) {
|
||||
// #ifndef APP-NVUE
|
||||
// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://uview.d3u.cn/js/getRect.html
|
||||
// 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同
|
||||
return new Promise(resolve => {
|
||||
this.$uGetRect(`.${el}`).then(size => {
|
||||
resolve(size)
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
// nvue下,使用dom模块查询元素高度
|
||||
// 返回一个promise,让调用此方法的主体能使用then回调
|
||||
return new Promise(resolve => {
|
||||
dom.getComponentRect(this.$refs[el][0], res => {
|
||||
resolve(res.size)
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
// 点击某一个日期
|
||||
clickHandler(index1, index2, item) {
|
||||
if (this.readonly) {
|
||||
return;
|
||||
}
|
||||
this.item = item
|
||||
const date = dayjs(item.date).format("YYYY-MM-DD")
|
||||
if (item.disabled) return
|
||||
// 对上一次选择的日期数组进行深度克隆
|
||||
let selected = uni.$u.deepClone(this.selected)
|
||||
if (this.mode === 'single') {
|
||||
// 单选情况下,让数组中的元素为当前点击的日期
|
||||
selected = [date]
|
||||
} else if (this.mode === 'multiple') {
|
||||
if (selected.some(item => this.dateSame(item, date))) {
|
||||
// 如果点击的日期已在数组中,则进行移除操作,也就是达到反选的效果
|
||||
const itemIndex = selected.findIndex(item => item === date)
|
||||
selected.splice(itemIndex, 1)
|
||||
} else {
|
||||
// 如果点击的日期不在数组中,且已有的长度小于总可选长度时,则添加到数组中去
|
||||
if (selected.length < this.maxCount) selected.push(date)
|
||||
}
|
||||
} else {
|
||||
// 选择区间形式
|
||||
if (selected.length === 0 || selected.length >= 2) {
|
||||
// 如果原来就为0或者大于2的长度,则当前点击的日期,就是开始日期
|
||||
selected = [date]
|
||||
} else if (selected.length === 1) {
|
||||
// 如果已经选择了开始日期
|
||||
const existsDate = selected[0]
|
||||
// 如果当前选择的日期小于上一次选择的日期,则当前的日期定为开始日期
|
||||
if (dayjs(date).isBefore(existsDate)) {
|
||||
selected = [date]
|
||||
} else if (dayjs(date).isAfter(existsDate)) {
|
||||
// 当前日期减去最大可选的日期天数,如果大于起始时间,则进行提示
|
||||
if (dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this.showRangePrompt) {
|
||||
if (this.rangePrompt) {
|
||||
uni.$u.toast(this.rangePrompt)
|
||||
} else {
|
||||
uni.$u.toast(`选择天数不能超过 ${this.maxRange} 天`)
|
||||
}
|
||||
return
|
||||
}
|
||||
// 如果当前日期大于已有日期,将当前的添加到数组尾部
|
||||
selected.push(date)
|
||||
const startDate = selected[0]
|
||||
const endDate = selected[1]
|
||||
const arr = []
|
||||
let i = 0
|
||||
do {
|
||||
// 将开始和结束日期之间的日期添加到数组中
|
||||
arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD"))
|
||||
i++
|
||||
// 累加的日期小于结束日期时,继续下一次的循环
|
||||
} while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate)))
|
||||
// 为了一次性修改数组,避免computed中多次触发,这里才用arr变量一次性赋值的方式,同时将最后一个日期添加近来
|
||||
arr.push(endDate)
|
||||
selected = arr
|
||||
} else {
|
||||
// 选择区间时,只有一个日期的情况下,且不允许选择起止为同一天的话,不允许选择自己
|
||||
if (selected[0] === date && !this.allowSameDay) return
|
||||
selected.push(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setSelected(selected)
|
||||
},
|
||||
// 设置默认日期
|
||||
setDefaultDate() {
|
||||
if (!this.defaultDate) {
|
||||
// 如果没有设置默认日期,则将当天日期设置为默认选中的日期
|
||||
const selected = [dayjs().format("YYYY-MM-DD")]
|
||||
return this.setSelected(selected, false)
|
||||
}
|
||||
let defaultDate = []
|
||||
const minDate = this.minDate || dayjs().format("YYYY-MM-DD")
|
||||
const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD")
|
||||
if (this.mode === 'single') {
|
||||
// 单选模式,可以是字符串或数组,Date对象等
|
||||
if (!uni.$u.test.array(this.defaultDate)) {
|
||||
defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")]
|
||||
} else {
|
||||
defaultDate = [this.defaultDate[0]]
|
||||
}
|
||||
} else {
|
||||
// 如果为非数组,则不执行
|
||||
if (!uni.$u.test.array(this.defaultDate)) return
|
||||
defaultDate = this.defaultDate
|
||||
}
|
||||
// 过滤用户传递的默认数组,取出只在可允许最大值与最小值之间的元素
|
||||
defaultDate = defaultDate.filter(item => {
|
||||
return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs(
|
||||
maxDate).add(1, 'day'))
|
||||
})
|
||||
this.setSelected(defaultDate, false)
|
||||
},
|
||||
setSelected(selected, event = true) {
|
||||
this.selected = selected
|
||||
event && this.$emit('monthSelected', this.selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/components.scss";
|
||||
|
||||
.u-calendar-month-wrapper {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.u-calendar-month {
|
||||
|
||||
&__title {
|
||||
font-size: 14px;
|
||||
line-height: 42px;
|
||||
height: 42px;
|
||||
color: $u-main-color;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__days {
|
||||
position: relative;
|
||||
@include flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&__month-mark-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@include flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&__text {
|
||||
font-size: 155px;
|
||||
color: rgba(231, 232, 234, 0.83);
|
||||
}
|
||||
}
|
||||
|
||||
&__day {
|
||||
@include flex;
|
||||
padding: 2px;
|
||||
/* #ifndef APP-NVUE */
|
||||
// vue下使用css进行宽度计算,因为某些安卓机会无法进行js获取父元素宽度进行计算得出,会有偏移
|
||||
width: calc(100% / 7);
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
|
||||
&__select {
|
||||
flex: 1;
|
||||
@include flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
&__dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 100px;
|
||||
background-color: $u-error;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 7px;
|
||||
}
|
||||
|
||||
&__buttom-info {
|
||||
color: $u-content-color;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
&--selected {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
color: #cacbcd;
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
|
||||
&--selected {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
color: #cacbcd;
|
||||
}
|
||||
}
|
||||
|
||||
&--selected {
|
||||
background-color: $u-primary;
|
||||
@include flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&--range-selected {
|
||||
opacity: 0.3;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&--range-start-selected {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
&--range-end-selected {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user