├── release_note.txt ├── .eslintignore ├── tests └── unit │ ├── .eslintrc.js │ └── utils │ ├── param2Obj.spec.js │ ├── formatTime.spec.js │ ├── parseTime.spec.js │ └── validate.spec.js ├── public ├── favicon.ico └── index.html ├── src ├── assets │ ├── app.png │ ├── chain.png │ ├── issue.png │ ├── QRCode.jpg │ ├── router.png │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ ├── icon-zy.svg │ ├── check-fail.svg │ ├── check-pass.svg │ ├── icon-bc.svg │ ├── icon-ly.svg │ ├── icon-sw.svg │ ├── nav-logo.svg │ ├── favicon.svg │ └── wecross.svg ├── styles │ ├── intro.scss │ ├── variables.scss │ ├── element-variables.scss │ ├── transition.scss │ ├── mixin.scss │ ├── element-ui.scss │ ├── index.scss │ └── sidebar.scss ├── layout │ ├── components │ │ ├── index.js │ │ ├── Sidebar │ │ │ ├── FixiOSBug.js │ │ │ ├── Link.vue │ │ │ ├── Item.vue │ │ │ ├── index.vue │ │ │ ├── Logo.vue │ │ │ └── SidebarItem.vue │ │ └── AppMain.vue │ └── mixin │ │ └── ResizeHandler.js ├── App.vue ├── utils │ ├── get-page-title.js │ ├── rsa.js │ ├── errorcode.js │ ├── clipboard.js │ ├── auth.js │ ├── authcode.js │ ├── transaction.js │ ├── validate.js │ ├── chainAccountIntro.js │ ├── resource.js │ ├── index.js │ ├── request.js │ ├── messageBox.js │ └── pem.js ├── .travis.yml ├── icons │ ├── index.js │ ├── svgo.yml │ └── svg │ │ ├── fullscreen.svg │ │ ├── user.svg │ │ ├── table.svg │ │ ├── search.svg │ │ ├── password.svg │ │ ├── eye.svg │ │ ├── question.svg │ │ ├── qrcode.svg │ │ ├── user-avatar.svg │ │ ├── eye-open.svg │ │ ├── exit-fullscreen.svg │ │ └── chicken.svg ├── store │ ├── getters.js │ ├── index.js │ └── modules │ │ ├── app.js │ │ ├── permission.js │ │ └── transaction.js ├── views │ ├── resource │ │ ├── resourceSteps │ │ │ ├── resourceManagerSteps.js │ │ │ └── resourceDeploySteps.js │ │ └── resourceManager.vue │ ├── transaction │ │ ├── transactionSteps │ │ │ ├── transactionManagerSteps.js │ │ │ └── xaTransactionStep.js │ │ └── transactionManager.vue │ ├── router │ │ ├── routerGuide.vue │ │ └── routerManager.vue │ └── document │ │ └── index.vue ├── components │ ├── Clipboard │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ ├── SvgIcon │ │ └── index.vue │ ├── Breadcrumb │ │ └── index.vue │ ├── ResourceShower │ │ └── index.vue │ ├── ChainExplorer │ │ └── index.vue │ └── HeaderSearch │ │ └── index.vue ├── main.js ├── api │ ├── status.js │ ├── conn.js │ ├── user.js │ ├── resource.js │ ├── ua.js │ ├── common.js │ └── transaction.js └── permission.js ├── .env.production ├── babel.config.js ├── plop-templates ├── utils.js ├── store │ ├── index.hbs │ └── prompt.js ├── view │ ├── index.hbs │ └── prompt.js └── component │ ├── index.hbs │ └── prompt.js ├── .env.development ├── docs └── images │ └── menu_logo_wecross.png ├── .travis.yml ├── .gitignore ├── plopfile.js ├── mock ├── index.js ├── utils.js ├── status.js ├── resource.js ├── mock-server.js ├── conn.js ├── ua.js └── user.js ├── .github └── workflows │ └── ci_check.yml ├── jest.config.js ├── CONTRIBUTING.md ├── Changelog.md ├── README.md ├── package.json └── vue.config.js /release_note.txt: -------------------------------------------------------------------------------- 1 | v1.4.0 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/src/assets/app.png -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/' 6 | 7 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/src/assets/chain.png -------------------------------------------------------------------------------- /src/assets/issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/src/assets/issue.png -------------------------------------------------------------------------------- /src/assets/QRCode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/src/assets/QRCode.jpg -------------------------------------------------------------------------------- /src/assets/router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/src/assets/router.png -------------------------------------------------------------------------------- /plop-templates/utils.js: -------------------------------------------------------------------------------- 1 | exports.notEmpty = name => v => 2 | !v || v.trim() === '' ? `${name} is required` : true 3 | -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/src/assets/404_images/404.png -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '' 6 | VUE_APP_MOCK_API = '/dev-api' -------------------------------------------------------------------------------- /docs/images/menu_logo_wecross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/docs/images/menu_logo_wecross.png -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeBankBlockchain/WeCross-WebApp/HEAD/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /src/styles/intro.scss: -------------------------------------------------------------------------------- 1 | .customTooltip { 2 | width: 300px; 3 | } 4 | 5 | .customTooltip .text-blue { 6 | color: #0366d6; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './Navbar' 2 | export { default as Sidebar } from './Sidebar' 3 | export { default as AppMain } from './AppMain' 4 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/utils/get-page-title.js: -------------------------------------------------------------------------------- 1 | const title = 'WeCross Web App' 2 | 3 | export default function getPageTitle(pageTitle) { 4 | if (pageTitle) { 5 | return `${pageTitle} - ${title}` 6 | } 7 | return `${title}` 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/rsa.js: -------------------------------------------------------------------------------- 1 | import JSEncrypt from 'jsencrypt' 2 | 3 | /** 4 | * rsa encrypt 5 | */ 6 | export function rsa_encode(input, pub) { 7 | const encryptor = new JSEncrypt(null) 8 | encryptor.setPublicKey(pub) 9 | return encryptor.encrypt(input) 10 | } 11 | -------------------------------------------------------------------------------- /src/.travis.yml: -------------------------------------------------------------------------------- 1 | # safelist 2 | branches: 3 | only: 4 | - /.*/ 5 | 6 | matrix: 7 | fast_finish: true 8 | include: 9 | 10 | - language: node_js 11 | node_js: 10 12 | script: npm run tests:ci 13 | os: linux 14 | dist: xenial 15 | -------------------------------------------------------------------------------- /plop-templates/store/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if state}} 2 | const state = {} 3 | {{/if}} 4 | 5 | {{#if mutations}} 6 | const mutations = {} 7 | {{/if}} 8 | 9 | {{#if actions}} 10 | const actions = {} 11 | {{/if}} 12 | 13 | export default { 14 | namespaced: true, 15 | {{options}} 16 | } 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # safelist 2 | branches: 3 | only: 4 | - /.*/ 5 | 6 | matrix: 7 | fast_finish: true 8 | include: 9 | 10 | - language: node_js 11 | node_js: 10 12 | script: npm install && npm run build:prod && npm run test:ci 13 | os: linux 14 | dist: xenial 15 | -------------------------------------------------------------------------------- /src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg component 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /src/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | package-lock.json 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /plopfile.js: -------------------------------------------------------------------------------- 1 | const viewGenerator = require('./plop-templates/view/prompt') 2 | const componentGenerator = require('./plop-templates/component/prompt') 3 | const storeGenerator = require('./plop-templates/store/prompt.js') 4 | 5 | module.exports = function(plop) { 6 | plop.setGenerator('view', viewGenerator) 7 | plop.setGenerator('component', componentGenerator) 8 | plop.setGenerator('store', storeGenerator) 9 | } 10 | -------------------------------------------------------------------------------- /mock/index.js: -------------------------------------------------------------------------------- 1 | const user = require('./user') 2 | const resource = require('./resource') 3 | const conn = require('./conn') 4 | const ua = require('./ua') 5 | const transaction = require('./transaction') 6 | const status = require('./status') 7 | 8 | const mocks = [ 9 | ...user, 10 | ...resource, 11 | ...conn, 12 | ...ua, 13 | ...transaction, 14 | ...status 15 | ] 16 | 17 | module.exports = { 18 | mocks 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plop-templates/view/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if template}} 2 | 5 | {{/if}} 6 | 7 | {{#if script}} 8 | 20 | {{/if}} 21 | 22 | {{#if style}} 23 | 26 | {{/if}} 27 | -------------------------------------------------------------------------------- /src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plop-templates/component/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if template}} 2 | 5 | {{/if}} 6 | 7 | {{#if script}} 8 | 20 | {{/if}} 21 | 22 | {{#if style}} 23 | 26 | {{/if}} 27 | -------------------------------------------------------------------------------- /tests/unit/utils/param2Obj.spec.js: -------------------------------------------------------------------------------- 1 | import { param2Obj } from '@/utils/index.js' 2 | describe('Utils:param2Obj', () => { 3 | const url = 'https://github.com/PanJiaChen/vue-element-admin?name=bill&age=29&sex=1&field=dGVzdA==&key=%E6%B5%8B%E8%AF%95' 4 | 5 | it('param2Obj test', () => { 6 | expect(param2Obj(url)).toEqual({ 7 | name: 'bill', 8 | age: '29', 9 | sex: '1', 10 | field: window.btoa('test'), 11 | key: '测试' 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | sidebar: state => state.app.sidebar, 3 | device: state => state.app.device, 4 | token: state => state.user.token, 5 | avatar: state => state.user.avatar, 6 | name: state => state.user.name, 7 | roles: state => state.user.roles, 8 | transactionID: state => state.transaction.transactionID, 9 | XAPaths: state => state.transaction.paths, 10 | permissionRoutes: state => state.permission.routes 11 | } 12 | export default getters 13 | -------------------------------------------------------------------------------- /src/assets/icon-zy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/ci_check.yml: -------------------------------------------------------------------------------- 1 | name: CI Check 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-node@v2-beta 11 | with: 12 | node-version: '10' 13 | - name: npm install 14 | run: npm install 15 | - name: Build production 16 | run: npm install && npm run build:prod 17 | - name: Test 18 | run: npm run test:ci 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import getters from './getters' 4 | import app from './modules/app' 5 | import user from './modules/user' 6 | import transaction from './modules/transaction' 7 | import permission from '@/store/modules/permission' 8 | 9 | Vue.use(Vuex) 10 | 11 | const store = new Vuex.Store({ 12 | modules: { 13 | app, 14 | user, 15 | transaction, 16 | permission 17 | }, 18 | getters 19 | }) 20 | 21 | export default store 22 | -------------------------------------------------------------------------------- /src/views/resource/resourceSteps/resourceManagerSteps.js: -------------------------------------------------------------------------------- 1 | export const resourceManagerSteps = [ 2 | { 3 | element: '#ChainExplorer', 4 | title: '1. 导航选择', 5 | intro: '第一步:从zone-chain导航中选择对应的链', 6 | position: 'right' 7 | }, 8 | { 9 | element: '#ResourceExplorer', 10 | title: '2. 资源列表展示', 11 | intro: '第二步:选择好对应的链之后,就会在资源列表中展示这条链中所有资源', 12 | position: 'left' 13 | }, 14 | { 15 | element: '#resourceDeploy', 16 | title: '资源部署', 17 | intro: '若需要部署资源,请点击"部署资源"按钮', 18 | position: 'left' 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /src/views/transaction/transactionSteps/transactionManagerSteps.js: -------------------------------------------------------------------------------- 1 | export const transactionManagerSteps = [ 2 | { 3 | element: '#ChainExplorer', 4 | title: '1. 导航选择', 5 | intro: '第一步:从zone-chain导航中选择对应的链', 6 | position: 'right' 7 | }, 8 | { 9 | element: '#TransactionListExplorer', 10 | title: '2. 交易列表展示', 11 | intro: '第二步:选择好对应的链之后,就会在资源列表中展示这条链中所有交易', 12 | position: 'left' 13 | }, 14 | { 15 | element: '#sendTransaction', 16 | title: '发起交易', 17 | intro: '若需要发起跨链交易,请点击"发交易"按钮', 18 | position: 'left' 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /src/utils/errorcode.js: -------------------------------------------------------------------------------- 1 | export const ErrorCode = { 2 | Success: 0, 3 | InvalidParameters: 40001, 4 | UAAccountExist: 40002, 5 | UAAccountNotExist: 40003, 6 | ChainAccountExist: 40004, 7 | ChainAccountNotExist: 40005, 8 | ConfigurationItemError: 40006, 9 | AccountOrPasswordIncorrect: 40007, 10 | ImageAuthTokenExpired: 40008, 11 | ImageAuthTokenNotExist: 40009, 12 | ImageAuthTokenNotMatch: 40010, 13 | CreateUAFailed: 40011, 14 | UserHasLogout: 40012, 15 | ChainAccountTypeNotFound: 40013, 16 | FlushDataException: 40014, 17 | UndefinedError: 40099 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/components/Clipboard/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | -------------------------------------------------------------------------------- /src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/FixiOSBug.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | device() { 4 | return this.$store.state.app.device 5 | } 6 | }, 7 | mounted() { 8 | this.fixBugIniOS() 9 | }, 10 | methods: { 11 | fixBugIniOS() { 12 | const $subMenu = this.$refs.subMenu 13 | if ($subMenu) { 14 | const handleMouseleave = $subMenu.handleMouseleave 15 | $subMenu.handleMouseleave = (e) => { 16 | if (this.device === 'mobile') { 17 | return 18 | } 19 | handleMouseleave(e) 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mock/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} url 3 | * @returns {Object} 4 | */ 5 | function param2Obj(url) { 6 | const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') 7 | if (!search) { 8 | return {} 9 | } 10 | const obj = {} 11 | const searchArr = search.split('&') 12 | searchArr.forEach(v => { 13 | const index = v.indexOf('=') 14 | if (index !== -1) { 15 | const name = v.substring(0, index) 16 | const val = v.substring(index + 1, v.length) 17 | obj[name] = val 18 | } 19 | }) 20 | return obj 21 | } 22 | 23 | module.exports = { 24 | param2Obj 25 | } 26 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import 'normalize.css/normalize.css' // A modern alternative to CSS resets 4 | 5 | import ElementUI from 'element-ui' 6 | import 'element-ui/lib/theme-chalk/index.css' 7 | 8 | import '@/styles/index.scss' // global css 9 | 10 | import App from './App' 11 | import store from './store' 12 | import router from './router' 13 | 14 | import '@/icons' // icon 15 | import '@/permission' // permission control 16 | 17 | Vue.use(ElementUI) 18 | 19 | Vue.config.productionTip = false 20 | 21 | new Vue({ 22 | el: '#app', 23 | router, 24 | store, 25 | render: h => h(App) 26 | }) 27 | -------------------------------------------------------------------------------- /src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/utils/clipboard.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Clipboard from 'clipboard' 3 | 4 | function clipboardSuccess() { 5 | Vue.prototype.$message({ 6 | message: '复制成功', 7 | type: 'success', 8 | duration: 1500 9 | }) 10 | } 11 | 12 | function clipboardError() { 13 | Vue.prototype.$message({ 14 | message: '复制失败', 15 | type: 'error' 16 | }) 17 | } 18 | 19 | export default function handleClipboard(text, event) { 20 | const clipboard = new Clipboard(event.target, { 21 | text: () => text 22 | }) 23 | clipboard.on('success', () => { 24 | clipboardSuccess() 25 | clipboard.destroy() 26 | }) 27 | clipboard.on('error', () => { 28 | clipboardError() 29 | clipboard.destroy() 30 | }) 31 | clipboard.onClick(event) 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/check-fail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | check-pass 9 | 12 | 13 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 6 | 'jest-transform-stub', 7 | '^.+\\.jsx?$': 'babel-jest' 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1' 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 15 | ], 16 | collectCoverageFrom: ['src/unit/**/*.{js,vue}', '!src/unit/auth.js', '!src/unit/request.js', 'src/components/**/*.{js,vue}'], 17 | coverageDirectory: '/tests/unit/coverage', 18 | // 'collectCoverage': true, 19 | 'coverageReporters': [ 20 | 'lcov', 21 | 'text-summary' 22 | ], 23 | testURL: 'http://localhost/' 24 | } 25 | -------------------------------------------------------------------------------- /src/api/status.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | /** 4 | * get the status of system 5 | * @return {Promise} an axios promise object of response 6 | */ 7 | export function systemStatus() { 8 | return request({ 9 | url: '/sys/systemStatus', 10 | method: 'get' 11 | }) 12 | } 13 | 14 | /** 15 | * list supported stub types in wecross 16 | * @return {Promise} an axios promise object of response 17 | */ 18 | export function supportedStubs(params) { 19 | return request({ 20 | url: '/sys/supportedStubs', 21 | method: 'get', 22 | params 23 | }) 24 | } 25 | 26 | /** 27 | * get wecross router status 28 | * @return {Promise} an axios promise object of response 29 | */ 30 | export function routerStatus() { 31 | return request({ 32 | url: '/sys/routerStatus', 33 | method: 'get' 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 44 | -------------------------------------------------------------------------------- /src/views/router/routerGuide.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 31 | 32 | 35 | -------------------------------------------------------------------------------- /src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // base color 2 | $blue:#324157; 3 | $light-blue:#3A71A8; 4 | $red:#C03639; 5 | $pink: #E65D6E; 6 | $green: #30B08F; 7 | $tiffany: #4AB7BD; 8 | $yellow:#FEC171; 9 | $panGreen: #30B08F; 10 | 11 | // sidebar 12 | $menuText:#bfcbd9; 13 | $menuActiveText:#409EFF; 14 | $subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 15 | 16 | $menuBg:#304156; 17 | $menuHover:#263445; 18 | 19 | $subMenuBg:#1f2d3d; 20 | $subMenuHover:#001528; 21 | 22 | $sideBarWidth: 190px; 23 | 24 | // the :export directive is the magic sauce for webpack 25 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 26 | :export { 27 | menuText: $menuText; 28 | menuActiveText: $menuActiveText; 29 | subMenuActiveText: $subMenuActiveText; 30 | menuBg: $menuBg; 31 | menuHover: $menuHover; 32 | subMenuBg: $subMenuBg; 33 | subMenuHover: $subMenuHover; 34 | sideBarWidth: $sideBarWidth; 35 | } 36 | -------------------------------------------------------------------------------- /src/styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * I think element-ui's default theme color is too light for long-term use. 3 | * So I modified the default color and you can modify it to your liking. 4 | **/ 5 | 6 | /* theme color */ 7 | $--color-primary: #1890ff; 8 | $--color-success: #13ce66; 9 | $--color-warning: #ffba00; 10 | $--color-danger: #ff4949; 11 | // $--color-info: #1E1E1E; 12 | 13 | $--button-font-weight: 400; 14 | 15 | // $--color-text-regular: #1f2d3d; 16 | 17 | $--border-color-light: #dfe4ed; 18 | $--border-color-lighter: #e6ebf5; 19 | 20 | $--table-border: 1px solid #dfe6ec; 21 | 22 | /* icon font path, required */ 23 | $--font-path: "~element-ui/lib/theme-chalk/fonts"; 24 | 25 | @import "~element-ui/packages/theme-chalk/src/index"; 26 | 27 | // the :export directive is the magic sauce for webpack 28 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 29 | :export { 30 | theme: $--color-primary; 31 | } 32 | -------------------------------------------------------------------------------- /src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // global transition css 2 | 3 | /* fade */ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.2s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /* fade-transform */ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .2s; 18 | } 19 | 20 | .fade-transform-enter { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /* breadcrumb transition */ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .2s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .2s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } 49 | -------------------------------------------------------------------------------- /src/assets/check-pass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | check-pass 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/icons/svg/question.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon-bc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 47 | -------------------------------------------------------------------------------- /src/assets/icon-ly.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 贡献代码 2 | 3 | 非常感谢能有心为WeCross贡献代码! 4 | 5 | ## 分支策略 6 | 7 | 项目采用[git-flow](https://jeffkreeftmeijer.com/git-flow/)的分支策略。 8 | 9 | * master:最新的稳定分支 10 | * dev:待发布的稳定分支 11 | * feature-xxxx:一个正在开发xxxx特性分支 12 | * bugfix-xxxx:一个正在修bug xxxx的分支 13 | 14 | ## 贡献方法 15 | 16 | ### Issue 17 | 18 | 可直接去[issues page](https://github.com/WeBankBlockchain/WeCross-WebApp/issues)提issue。 19 | 20 | ### 修复bug 21 | 22 | 1. Fork本仓库到个人仓库 23 | 2. 从个人仓库的master分支拉出一个bugfix-xxxx分支 24 | 3. 在bugfix-xxxx上修复bug 25 | 4. 测试修复的bug 26 | 5. PR(Pull Request)到本仓库的dev分支 27 | 6. 等待社区review这个PR 28 | 7. PR合入,bug修复完成! 29 | 30 | ### 开发新特性 31 | 32 | 1. Fork本仓库到个人仓库 33 | 2. 从个人仓库的dev分支拉出一个feature-xxxx分支 34 | 3. 在feature-xxxx上进行特性开发 35 | 4. 不定期的从本仓库的dev分支pull最新的改动到feature-xxxx分支 36 | 5. 测试新特性 37 | 6. PR(Pull Request)到本参考的dev分支 38 | 7. 等待社区review这个PR 39 | 8. PR合入,特性开发完成! 40 | 41 | ## 代码格式化 42 | 43 | 代码格式化gradle插件[google-java-format-gradle-plugin](https://github.com/sherter/google-java-format-gradle-plugin). 44 | 45 | 执行任务 `googleJavaFormat`格式化java文件。 46 | ``` 47 | ./gradlew goJF 48 | ``` 49 | 执行任务 `verifyGoogleJavaFormat`验证java文件是否格式化完成 50 | ``` 51 | ./gradlew verGJF 52 | ``` 53 | -------------------------------------------------------------------------------- /tests/unit/utils/formatTime.spec.js: -------------------------------------------------------------------------------- 1 | import { formatTime } from '@/utils/index.js' 2 | describe('Utils:formatTime', () => { 3 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 4 | const retrofit = 5 * 1000 5 | 6 | it('ten digits timestamp', () => { 7 | expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分') 8 | }) 9 | it('test now', () => { 10 | expect(formatTime(+new Date() - 1)).toBe('刚刚') 11 | }) 12 | it('less two minute', () => { 13 | expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前') 14 | }) 15 | it('less two hour', () => { 16 | expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前') 17 | }) 18 | it('less one day', () => { 19 | expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前') 20 | }) 21 | it('more than one day', () => { 22 | expect(formatTime(d)).toBe('7月13日17时54分') 23 | }) 24 | it('format', () => { 25 | expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 26 | expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 27 | expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | ### v1.4.0 2 | 3 | (2024-03-01) 4 | 5 | **新增** 6 | 7 | - 适配 `systemStatus` 的GET接口,获取系统信息在首页展示 https://github.com/WeBankBlockchain/WeCross-WebApp/pull/180 8 | - 事务列表页面新增按链查询的功能,优化事务查询效率 https://github.com/WeBankBlockchain/WeCross-WebApp/pull/182 9 | 10 | ### v1.3.1 11 | 12 | (2023-07-31) 13 | 14 | **新增** 15 | 16 | * 新增对FISCO BCOS 3.x版本WASM合约的部署调用 17 | 18 | ### v1.3.0 19 | 20 | (2023-03-15) 21 | 22 | **新增** 23 | 24 | * 新增对FISCO BCOS 3.x版本的支持 25 | * 新增Email登陆注册检查的功能 26 | 27 | ### v1.2.1 28 | 29 | (2021-12-15) 30 | 31 | (无改动,配合其他组件同步更新版本) 32 | 33 | ### v1.2.0 34 | 35 | (2021-08-20) 36 | 37 | **新增** 38 | 39 | * 资源访问控制功能,管理员可通过网页管理台给用户授权可访问的资源 40 | 41 | ### v1.1.1 42 | 43 | (2021-04-02) 44 | 45 | **更改** 46 | 47 | * 增加内容复制按钮 48 | * 用户操作优化 49 | * 展示效果优化 50 | 51 | ### v1.1.0 52 | 53 | (2020-02-02) 54 | 55 | **新增** 56 | 57 | * 添加修改密码功能 58 | * 添加页面帮助指引 59 | 60 | **更新** 61 | 62 | * 体验优化 63 | * 问题修复 64 | 65 | ### v1.0.0 66 | 67 | (2020-12-17) 68 | 69 | **功能** 70 | 71 | * WeCross管理台:提供可视化的跨链管理服务 72 | 73 | **新增** 74 | 75 | * 注册与登录页:注册跨链账户,登录管理平台 76 | * 平台首页:展示跨链网络信息以及系统配置信息 77 | * 账户管理页:跨链账户生命周期管理 78 | * 路由管理页:互联的跨链路由管理 79 | * 资源管理页:跨链资源展示以及资源调用 80 | * 交易管理页:跨链交易列表以及交易详情展示 81 | * 事务管理页:事务列表展示以及事务操作 82 | -------------------------------------------------------------------------------- /src/utils/auth.js: -------------------------------------------------------------------------------- 1 | 2 | const TokenKey = 'wecross-token' 3 | const UsernameKey = 'wecross-user' 4 | const PubKey = 'wecross-pub' 5 | 6 | export function getToken() { 7 | return localStorage.getItem(TokenKey) 8 | } 9 | 10 | export function setToken(token) { 11 | return localStorage.setItem(TokenKey, token) 12 | } 13 | 14 | export function removeToken() { 15 | return localStorage.removeItem(TokenKey) 16 | } 17 | 18 | export function getUsername() { 19 | return localStorage.getItem(UsernameKey) 20 | } 21 | 22 | export function setUsername(username) { 23 | return localStorage.setItem(UsernameKey, username) 24 | } 25 | 26 | export function removeUsername() { 27 | return localStorage.removeItem(UsernameKey) 28 | } 29 | 30 | export function isUserFirstTimeUse(username) { 31 | // not first time 32 | if (localStorage.getItem(username)) { 33 | return false 34 | } else { 35 | localStorage.setItem(username, '1') 36 | return true 37 | } 38 | } 39 | 40 | export function getPubKey(pub) { 41 | return localStorage.getItem(PubKey) 42 | } 43 | 44 | export function setPubKey(pub) { 45 | return localStorage.setItem(PubKey, pub) 46 | } 47 | 48 | export function removePubKey() { 49 | return localStorage.removeItem(PubKey) 50 | } 51 | -------------------------------------------------------------------------------- /src/icons/svg/qrcode.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/user-avatar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/utils/parseTime.spec.js: -------------------------------------------------------------------------------- 1 | import { parseTime } from '@/utils/index.js' 2 | 3 | describe('Utils:parseTime', () => { 4 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 5 | it('timestamp', () => { 6 | expect(parseTime(d)).toBe('2018-07-13 17:54:01') 7 | }) 8 | 9 | it('timestamp string', () => { 10 | expect(parseTime((d + ''))).toBe('2018-07-13 17:54:01') 11 | }) 12 | 13 | it('ten digits timestamp', () => { 14 | expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01') 15 | }) 16 | it('new Date', () => { 17 | expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01') 18 | }) 19 | it('format', () => { 20 | expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 21 | expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 22 | expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 23 | }) 24 | it('get the day of the week', () => { 25 | expect(parseTime(d, '{a}')).toBe('五') // 星期五 26 | }) 27 | it('get the day of the week', () => { 28 | expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日 29 | }) 30 | it('empty argument', () => { 31 | expect(parseTime()).toBeNull() 32 | }) 33 | 34 | it('null', () => { 35 | expect(parseTime(null)).toBeNull() 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /src/views/transaction/transactionSteps/xaTransactionStep.js: -------------------------------------------------------------------------------- 1 | export const xaTransactionManagerSteps = [ 2 | { 3 | element: '#ChainExplorer', 4 | title: '1. 导航选择', 5 | intro: '第一步:从zone-chain导航中选择对应的链', 6 | position: 'right' 7 | }, 8 | { 9 | element: '#xaTransactionListExplorer', 10 | title: '2. 事务列表展示', 11 | intro: '第二步:选择好对应的链之后,就会在资源列表中展示这条链中所有事务', 12 | position: 'left' 13 | }, 14 | { 15 | element: '#sendxaTransaction', 16 | title: '发起交易', 17 | intro: '若需要发起事务,请点击"发起事务"按钮', 18 | position: 'left' 19 | } 20 | ] 21 | 22 | export const startXASteps = [ 23 | { 24 | element: '#XAID', 25 | title: '开启事务', 26 | intro: '第一步:生成事务ID!
只支持输入16进制', 27 | position: 'bottom' 28 | }, 29 | { 30 | element: '#XAPath', 31 | title: '开启事务', 32 | intro: '第二步:选择事务资源!
从左边待选资源列表勾选资源,点击添加按钮到已选资源列表', 33 | position: 'top' 34 | }, 35 | { 36 | element: '#btnGroup', 37 | title: '开启事务', 38 | intro: '第三步:点击开启事务!', 39 | position: 'top' 40 | } 41 | ] 42 | 43 | export const execXASteps = [ 44 | { 45 | element: '#xaForm', 46 | title: '执行事务', 47 | intro: '执行事务交易!
填写资源、方法和参数,执行调用', 48 | position: 'top' 49 | }, 50 | { 51 | element: '#xaList', 52 | title: '执行事务', 53 | intro: '查看事务步骤!
查看事务详细步骤', 54 | position: 'top' 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 37 | 38 | 50 | 51 | 59 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 45 | -------------------------------------------------------------------------------- /src/assets/icon-sw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /plop-templates/view/prompt.js: -------------------------------------------------------------------------------- 1 | const { notEmpty } = require('../utils.js') 2 | 3 | module.exports = { 4 | description: 'generate a view', 5 | prompts: [{ 6 | type: 'input', 7 | name: 'name', 8 | message: 'view name please', 9 | validate: notEmpty('name') 10 | }, 11 | { 12 | type: 'checkbox', 13 | name: 'blocks', 14 | message: 'Blocks:', 15 | choices: [{ 16 | name: '