diff --git a/packages/server/certd-client b/packages/server/certd-client deleted file mode 160000 index 8c7b3853b..000000000 --- a/packages/server/certd-client +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8c7b3853be27df392dff765d39c6c53de09418bd diff --git a/packages/server/certd-server b/packages/server/certd-server deleted file mode 160000 index a5681f154..000000000 --- a/packages/server/certd-server +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a5681f154468fc5c4aac7e4a0ce090cfffc00e9e diff --git a/packages/ui/certd-client/.browserslistrc b/packages/ui/certd-client/.browserslistrc new file mode 100644 index 000000000..214388fe4 --- /dev/null +++ b/packages/ui/certd-client/.browserslistrc @@ -0,0 +1,3 @@ +> 1% +last 2 versions +not dead diff --git a/packages/ui/certd-client/.env b/packages/ui/certd-client/.env new file mode 100644 index 000000000..4933b20fc --- /dev/null +++ b/packages/ui/certd-client/.env @@ -0,0 +1,3 @@ +VITE_APP_API=/api +#登录与权限关闭 +VITE_APP_PM_ENABLED=true diff --git a/packages/ui/certd-client/.env.debug b/packages/ui/certd-client/.env.debug new file mode 100644 index 000000000..6a532d749 --- /dev/null +++ b/packages/ui/certd-client/.env.debug @@ -0,0 +1,2 @@ +#登录与权限开启 +VITE_APP_PM_ENABLED=false diff --git a/packages/ui/certd-client/.env.debugpm b/packages/ui/certd-client/.env.debugpm new file mode 100644 index 000000000..c3b69a80f --- /dev/null +++ b/packages/ui/certd-client/.env.debugpm @@ -0,0 +1,2 @@ +#登录与权限开启 +VITE_APP_PM_ENABLED=true diff --git a/packages/ui/certd-client/.env.pm b/packages/ui/certd-client/.env.pm new file mode 100644 index 000000000..c3b69a80f --- /dev/null +++ b/packages/ui/certd-client/.env.pm @@ -0,0 +1,2 @@ +#登录与权限开启 +VITE_APP_PM_ENABLED=true diff --git a/packages/ui/certd-client/.env.production b/packages/ui/certd-client/.env.production new file mode 100644 index 000000000..bc3dbcb73 --- /dev/null +++ b/packages/ui/certd-client/.env.production @@ -0,0 +1,3 @@ +VITE_APP_API=http://www.docmirror.cn:7001/api +#登录与权限开启 +VITE_APP_PM_ENABLED=true diff --git a/packages/ui/certd-client/.eslintignore b/packages/ui/certd-client/.eslintignore new file mode 100644 index 000000000..eb79dd5fc --- /dev/null +++ b/packages/ui/certd-client/.eslintignore @@ -0,0 +1,2 @@ +node_modules +.idea diff --git a/packages/ui/certd-client/.eslintrc.js b/packages/ui/certd-client/.eslintrc.js new file mode 100644 index 000000000..2dcd3abe2 --- /dev/null +++ b/packages/ui/certd-client/.eslintrc.js @@ -0,0 +1,76 @@ +module.exports = { + root: true, + env: { + browser: true, + node: true, + es6: true + }, + parser: "vue-eslint-parser", + parserOptions: { + parser: "@typescript-eslint/parser", + ecmaVersion: 2020, + sourceType: "module", + jsxPragma: "React", + ecmaFeatures: { + jsx: true, + tsx: true + } + }, + extends: [ + "plugin:vue/vue3-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + "prettier" + ], + rules: { + //"max-len": [0, 200, 2, { ignoreUrls: true }], + "@typescript-eslint/ban-ts-ignore": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off" + // "@typescript-eslint/no-unused-vars": [ + // "error", + // { + // argsIgnorePattern: "^h$", + // varsIgnorePattern: "^h$", + // }, + // ], + // "no-unused-vars": [ + // "error", + // { + // argsIgnorePattern: "^h$", + // varsIgnorePattern: "^h$", + // }, + // ], + // "vue/custom-event-name-casing": "off", + // "no-use-before-define": "off", + // "space-before-function-paren": "off", + + // "vue/attributes-order": "off", + // "vue/one-component-per-file": "off", + // "vue/html-closing-bracket-newline": "off", + // "vue/max-attributes-per-line": "off", + // "vue/multiline-html-element-content-newline": "off", + // "vue/singleline-html-element-content-newline": "off", + // "vue/attribute-hyphenation": "off", + // "vue/require-default-prop": "off", + // "vue/html-self-closing": [ + // "error", + // { + // html: { + // void: "always", + // normal: "never", + // component: "always", + // }, + // svg: "always", + // math: "always", + // }, + // ], + } +}; diff --git a/packages/ui/certd-client/.gitignore b/packages/ui/certd-client/.gitignore new file mode 100644 index 000000000..980be95df --- /dev/null +++ b/packages/ui/certd-client/.gitignore @@ -0,0 +1,11 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +/stats.html +yarn.lock +.idea +/.idea/ +yarn-error.log +vite-profile.cpuprofile diff --git a/packages/ui/certd-client/.npmignore b/packages/ui/certd-client/.npmignore new file mode 100644 index 000000000..b6c09bbd8 --- /dev/null +++ b/packages/ui/certd-client/.npmignore @@ -0,0 +1,2 @@ +node_modules +/stats.html diff --git a/packages/ui/certd-client/.prettierrc b/packages/ui/certd-client/.prettierrc new file mode 100644 index 000000000..61e99f7c5 --- /dev/null +++ b/packages/ui/certd-client/.prettierrc @@ -0,0 +1,5 @@ +{ + + "trailingComma": "none", + "printWidth": 220 +} diff --git a/packages/ui/certd-client/CHANGELOG.md b/packages/ui/certd-client/CHANGELOG.md new file mode 100644 index 000000000..ccaa88d14 --- /dev/null +++ b/packages/ui/certd-client/CHANGELOG.md @@ -0,0 +1,12 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.10.5](https://github.com/fast-crud/fast-crud/compare/v0.10.4...v0.10.5) (2021-07-01) + + +### Performance Improvements + +* fs-admin 与crud demo ([4e6b20f](https://github.com/fast-crud/fast-crud/commit/4e6b20fe19434460853841f371b9fd5f16e5e2d3)) +* fs-admin纳入子模块 ([2940d30](https://github.com/fast-crud/fast-crud/commit/2940d30f419bf4bde1e8e791f1fbdb9184818285)) diff --git a/packages/ui/certd-client/LICENSE b/packages/ui/certd-client/LICENSE new file mode 100644 index 000000000..0ad25db4b --- /dev/null +++ b/packages/ui/certd-client/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/packages/ui/certd-client/README.md b/packages/ui/certd-client/README.md new file mode 100644 index 000000000..600173994 --- /dev/null +++ b/packages/ui/certd-client/README.md @@ -0,0 +1,27 @@ +# fs-admin-antdv + +基于vue3、antdv 的 admin管理后台脚手架 +更多信息请参考: [fast-crud](https://github.com/fast-crud/fast-crud) +# server端 + +## fs-server-js +https://github.com/fast-crud/fs-server-js + + +# 感谢 + +### 依赖 +* [vue](https://github.com/vuejs/vue-next) +* [vue-router](https://github.com/vuejs/vue-router-next) +* [antdv 2x](https://github.com/vueComponent/ant-design-vue) +* [vitejs](https://github.com/vitejs/vite) +* [pinia](https://github.com/posva/pinia) +* [purge-icons](https://github.com/antfu/purge-icons) + +### 参考如下项目 +* [d2-admin](https://github.com/d2-projects/d2-admin) +* [antdv-pro](https://github.com/vueComponent/ant-design-vue-pro) +* [vben-admin](https://github.com/anncwb/vue-vben-admin) + +感谢这些优秀的项目 + diff --git a/packages/ui/certd-client/build/modify-vars.ts b/packages/ui/certd-client/build/modify-vars.ts new file mode 100644 index 000000000..c6cf20a37 --- /dev/null +++ b/packages/ui/certd-client/build/modify-vars.ts @@ -0,0 +1,11 @@ +// import { getThemeVariables } from "ant-design-vue/dist/theme"; +// import path from "path"; +// const resolve = path.resolve; +export function generateModifyVars(dark = false) { + //const modifyVars = getThemeVariables({ dark }); + // const vars = `${resolve("src/style/theme/index.less")}`; + return { + //...modifyVars + // hack: `true; @import (reference) "${vars}";` + }; +} diff --git a/packages/ui/certd-client/build/theme-colors.ts b/packages/ui/certd-client/build/theme-colors.ts new file mode 100644 index 000000000..41396ef06 --- /dev/null +++ b/packages/ui/certd-client/build/theme-colors.ts @@ -0,0 +1,72 @@ +import { generate } from "@ant-design/colors"; + +export const primaryColor = "#1890ff"; + +export const darkMode = "light"; + +type Fn = (...arg: any) => any; + +export interface GenerateColorsParams { + mixLighten: Fn; + mixDarken: Fn; + tinycolor: any; + color?: string; +} + +export function generateAntColors(color: string) { + return generate(color, { + theme: "default" + }); +} + +export function getThemeColors(color?: string) { + const tc = color || primaryColor; + const colors = generateAntColors(tc); + const primary = colors[5]; + const modeColors = generateAntColors(primary); + + return [...colors, ...modeColors]; +} + +export function generateColors({ color = primaryColor, mixLighten, mixDarken, tinycolor }: GenerateColorsParams) { + const arr = new Array(19).fill(0); + const lightens = arr.map((_t, i) => { + return mixLighten(color, i / 5); + }); + + const darkens = arr.map((_t, i) => { + return mixDarken(color, i / 5); + }); + + const alphaColors = arr.map((_t, i) => { + return tinycolor(color) + .setAlpha(i / 20) + .toRgbString(); + }); + + const shortAlphaColors = alphaColors.map((item) => item.replace(/\s/g, "").replace(/0\./g, ".")); + + const tinycolorLightens = arr + .map((_t, i) => { + return tinycolor(color) + .lighten(i * 5) + .toHexString(); + }) + .filter((item) => item !== "#ffffff"); + + const tinycolorDarkens = arr + .map((_t, i) => { + return tinycolor(color) + .darken(i * 5) + .toHexString(); + }) + .filter((item) => item !== "#000000"); + return [ + ...lightens, + ...darkens, + ...alphaColors, + ...shortAlphaColors, + ...tinycolorDarkens, + ...tinycolorLightens + ].filter((item) => !item.includes("-")); +} diff --git a/packages/ui/certd-client/build/theme-plugin.ts b/packages/ui/certd-client/build/theme-plugin.ts new file mode 100644 index 000000000..d223642b4 --- /dev/null +++ b/packages/ui/certd-client/build/theme-plugin.ts @@ -0,0 +1,68 @@ +/** + * Vite plugin for website theme color switching + * https://github.com/anncwb/vite-plugin-theme + */ +import type { Plugin } from "vite"; +import path from "path"; +import { viteThemePlugin, mixLighten, mixDarken, tinycolor, antdDarkThemePlugin } from "vite-plugin-theme"; +import { getThemeColors, generateColors } from "./theme-colors"; +import { generateModifyVars } from "./modify-vars"; + +export function configThemePlugin(isBuild: boolean): Plugin[] { + const colors = generateColors({ + mixDarken, + mixLighten, + tinycolor + }); + const colorVariables = [...getThemeColors(), ...colors]; + const plugin = [ + viteThemePlugin({ + // resolveSelector: (s) => { + // s = s.trim(); + // switch (s) { + // case ".ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon": + // return ".ant-steps-item-icon > .ant-steps-icon"; + // case ".ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)": + // case ".ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover": + // case ".ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active": + // return s; + // case ".ant-steps-item-icon > .ant-steps-icon": + // return s; + // } + // return `[data-theme] ${s}`; + // }, + resolveSelector: (s) => { + s = s.trim(); + if (s === ".ant-btn:hover,.ant-btn:focus") { + // console.log("ssss", s); + return ".theme-discard-xxxxxxx"; + } + return s; + }, + colorVariables + }), + antdDarkThemePlugin({ + preloadFiles: [ + path.resolve(process.cwd(), "node_modules/ant-design-vue/dist/antd.less"), + path.resolve(process.cwd(), "src/style/theme/index.less") + ], + filter: (id) => (isBuild ? !id.endsWith("antd.less") : true), + // extractCss: false, + darkModifyVars: { + ...generateModifyVars(true), + "text-color": "#c9d1d9", + "text-color-base": "#c9d1d9", + "component-background": "#151515", + // black: '#0e1117', + // #8b949e + "text-color-secondary": "#8b949e", + "border-color-base": "#303030", + // 'border-color-split': '#30363d', + "item-active-bg": "#111b26", + "app-content-background": "rgb(255 255 255 / 4%)" + } + }) + ]; + + return (plugin as unknown) as Plugin[]; +} diff --git a/packages/ui/certd-client/index.html b/packages/ui/certd-client/index.html new file mode 100644 index 000000000..aac3b8bfe --- /dev/null +++ b/packages/ui/certd-client/index.html @@ -0,0 +1,26 @@ + + + + + + + antdv-fast-crud + + + + + + + diff --git a/packages/ui/certd-client/package.json b/packages/ui/certd-client/package.json new file mode 100644 index 000000000..644a7cac6 --- /dev/null +++ b/packages/ui/certd-client/package.json @@ -0,0 +1,120 @@ +{ + "name": "@fast-crud/fs-admin-antdv", + "version": "1.8.1", + "private": true, + "scripts": { + "dev": "vite", + "dev:pm": "vite --mode pm", + "dev:force": "vite --force", + "debug": "vite --mode debug", + "debug:pm": "vite --mode debugpm", + "debug:force": "vite --force --mode debug", + "build": "vite build ", + "serve": "vite preview", + "preview": "vite preview", + "pretty-quick": "pretty-quick", + "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/", + "upgrade": "yarn upgrade-interactive --latest" + }, + "author": "Greper", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-vue": "^6.0.1", + "@fast-crud/fast-crud": "^1.8.1", + "@fast-crud/fast-extends": "^1.7.2", + "@fast-crud/ui-antdv": "^1.8.1", + "@iconify/iconify": "^3.0.1", + "@iconify/json": "^2.1.151", + "@purge-icons/generated": "^0.9.0", + "@soerenmartius/vue3-clipboard": "^0.1.2", + "ant-design-vue": "^3.2.15", + "axios": "^1.2.1", + "axios-mock-adapter": "^1.21.2", + "base64-js": "^1.5.1", + "better-scroll": "^2.5.0", + "china-division": "^2.4.0", + "core-js": "^3.26.1", + "cos-js-sdk-v5": "^1.4.15-beta.0", + "cropperjs": "^1.5.13", + "deepdash-es": "5.3.5", + "highlight.js": "^11.7.0", + "humanize-duration": "^3.27.3", + "lodash-es": "^4.17.15", + "mitt": "^3.0.0", + "nanoid": "^4.0.0", + "nprogress": "^0.2.0", + "object-assign": "^4.1.1", + "pinia": "2.0.28", + "qiniu-js": "^3.4.1", + "sortablejs": "^1.14.0", + "vue": "^3.2.45", + "vue-cropperjs": "^5.0.0", + "vue-i18n": "^9.2.2", + "vue-router": "^4.1.5", + "vuedraggable": "^4.0.1" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^23.0.4", + "@rollup/plugin-node-resolve": "^15.0.1", + "@types/chai": "^4.3.4", + "@types/lodash-es": "^4.17.6", + "@types/mocha": "^10.0.1", + "@types/node": "^18.11.15", + "@typescript-eslint/eslint-plugin": "^5.46.1", + "@typescript-eslint/parser": "^5.46.1", + "@vitejs/plugin-legacy": "^3.0.1", + "@vitejs/plugin-vue": "^4.0.0", + "@vitejs/plugin-vue-jsx": "^3.0.0", + "@vue/compiler-sfc": "^3.2.45", + "@vue/eslint-config-typescript": "^11.0.2", + "@vue/test-utils": "^2.2.6", + "autoprefixer": "^10.4.12", + "caller-path": "^4.0.0", + "chai": "^4.3.7", + "eslint": "8.29.0", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-promise": "^6.0.1", + "eslint-plugin-vue": "^9.8.0", + "esno": "^0.16.3", + "husky": "^8.0.2", + "less": "^4.1.3", + "less-loader": "^11.0.0", + "lint-staged": "^13.1.0", + "postcss": "^8.4.20", + "prettier": "2.8.1", + "pretty-quick": "^3.1.3", + "rimraf": "^3.0.2", + "rollup": "^3.7.4", + "rollup-plugin-visualizer": "^5.8.2", + "stylelint": "^14.16.0", + "stylelint-config-prettier": "^9.0.4", + "stylelint-order": "^5.0.0", + "tailwindcss": "^3.2.4", + "ts-node": "^10.9.1", + "typescript": "4.9.4", + "vite": "^4.0.1", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-purge-icons": "^0.9.2", + "vite-plugin-theme": "^0.8.1", + "vite-plugin-windicss": "^1.8.10", + "vue-eslint-parser": "^9.1.0", + "windicss": "^3.5.6" + }, + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } + }, + "gitHead": "9c2162697f3affea22c9a8cbc0ca74f4034ab27e", + "vite": { + "optimizeDeps": { + "include": [ + "@iconify/iconify" + ] + } + } +} diff --git a/packages/ui/certd-client/public/favicon.ico b/packages/ui/certd-client/public/favicon.ico new file mode 100644 index 000000000..df36fcfb7 Binary files /dev/null and b/packages/ui/certd-client/public/favicon.ico differ diff --git a/packages/ui/certd-client/public/images/logo-certd/logo.svg b/packages/ui/certd-client/public/images/logo-certd/logo.svg new file mode 100644 index 000000000..edcdefbed --- /dev/null +++ b/packages/ui/certd-client/public/images/logo-certd/logo.svg @@ -0,0 +1,7 @@ + + + diff --git a/packages/ui/certd-client/public/images/logo-certd/rect-black.svg b/packages/ui/certd-client/public/images/logo-certd/rect-black.svg new file mode 100644 index 000000000..19bf439f6 --- /dev/null +++ b/packages/ui/certd-client/public/images/logo-certd/rect-black.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 让你的证书永不过期 + + + diff --git a/packages/ui/certd-client/public/images/logo-certd/rect.svg b/packages/ui/certd-client/public/images/logo-certd/rect.svg new file mode 100644 index 000000000..acc2f41c7 --- /dev/null +++ b/packages/ui/certd-client/public/images/logo-certd/rect.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 让你的证书永不过期 + + + diff --git a/packages/ui/certd-client/public/images/logo-certd/square.svg b/packages/ui/certd-client/public/images/logo-certd/square.svg new file mode 100644 index 000000000..026dd978f --- /dev/null +++ b/packages/ui/certd-client/public/images/logo-certd/square.svg @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/packages/ui/certd-client/public/images/logo/logo.svg b/packages/ui/certd-client/public/images/logo/logo.svg new file mode 100644 index 000000000..edcdefbed --- /dev/null +++ b/packages/ui/certd-client/public/images/logo/logo.svg @@ -0,0 +1,7 @@ + + + diff --git a/packages/ui/certd-client/public/images/logo/rect-black.svg b/packages/ui/certd-client/public/images/logo/rect-black.svg new file mode 100644 index 000000000..a57352ec1 --- /dev/null +++ b/packages/ui/certd-client/public/images/logo/rect-black.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/certd-client/public/images/logo/rect.svg b/packages/ui/certd-client/public/images/logo/rect.svg new file mode 100644 index 000000000..acc2f41c7 --- /dev/null +++ b/packages/ui/certd-client/public/images/logo/rect.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 让你的证书永不过期 + + + diff --git a/packages/ui/certd-client/public/images/logo/square.svg b/packages/ui/certd-client/public/images/logo/square.svg new file mode 100644 index 000000000..1b1395a16 --- /dev/null +++ b/packages/ui/certd-client/public/images/logo/square.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/certd-client/public/images/plugin.png b/packages/ui/certd-client/public/images/plugin.png new file mode 100644 index 000000000..23fa727cd Binary files /dev/null and b/packages/ui/certd-client/public/images/plugin.png differ diff --git a/packages/ui/certd-client/public/index.css b/packages/ui/certd-client/public/index.css new file mode 100644 index 000000000..0950b0504 --- /dev/null +++ b/packages/ui/certd-client/public/index.css @@ -0,0 +1,12 @@ +html, body, #app { height: 100%; margin: 0; padding: 0; width: 100%;} +.fs-bootstrap { background-color: #474949; height: 100%; display: flex; flex-direction: column;position: fixed;width: 100% } +.fs-bootstrap__main {flex:1; user-select: none; width: 100%; flex-grow: 1; display: flex; justify-content: center; align-items: center; flex-direction: column; } +.fs-bootstrap__footer { width: 100%; flex-grow: 0; text-align: center; padding: 10px 0; } +.fs-bootstrap__footer > a { font-size: 12px; color: #ABABAB; text-decoration: none; } +.fs-bootstrap__loading {box-sizing: border-box; height: 50px; width: 50px; margin-bottom: 5px;border:5px solid #333333;border-bottom:#aaa 5px solid; + border-radius:1000px; animation:load 1.1s infinite linear;-webkit-animation:load 1.1s infinite linear;-moz-animation:load 1.1s infinite linear; -o-animation:load 1.1s infinite linear; +} +@keyframes load {from {transform:rotate(0deg);-ms-transform:rotate(0deg);}to { transform:rotate(360deg);-ms-transform:rotate(360deg); } +}@-webkit-keyframes load {from {-webkit-transform:rotate(0deg); }to { -webkit-transform:rotate(360deg);} + }@-moz-keyframes load { from { -moz-transform:rotate(0deg); } to { -moz-transform:rotate(360deg);} + }@-o-keyframes load { from { -o-transform:rotate(0deg);} to { -o-transform:rotate(360deg);}} diff --git a/packages/ui/certd-client/public/index.html b/packages/ui/certd-client/public/index.html new file mode 100644 index 000000000..3e5a13962 --- /dev/null +++ b/packages/ui/certd-client/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + <%= htmlWebpackPlugin.options.title %> + + + +
+ + + diff --git a/packages/ui/certd-client/public/logo.svg b/packages/ui/certd-client/public/logo.svg new file mode 100644 index 000000000..1b1395a16 --- /dev/null +++ b/packages/ui/certd-client/public/logo.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/certd-client/src/App.vue b/packages/ui/certd-client/src/App.vue new file mode 100644 index 000000000..5059cfa5c --- /dev/null +++ b/packages/ui/certd-client/src/App.vue @@ -0,0 +1,56 @@ + + + diff --git a/packages/ui/certd-client/src/api/modules/api.user.mock.ts b/packages/ui/certd-client/src/api/modules/api.user.mock.ts new file mode 100644 index 000000000..53411901b --- /dev/null +++ b/packages/ui/certd-client/src/api/modules/api.user.mock.ts @@ -0,0 +1,31 @@ +export default [ + { + path: "/login", + method: "post", + handle() { + return { + code: 0, + msg: "success", + data: { + token: "faker token", + expire: 10000 + } + }; + } + }, + { + path: "/sys/authority/user/mine", + method: "get", + handle() { + return { + code: 0, + msg: "success", + data: { + id: 1, + username: "username", + nickName: "admin" + } + }; + } + } +]; diff --git a/packages/ui/certd-client/src/api/modules/api.user.ts b/packages/ui/certd-client/src/api/modules/api.user.ts new file mode 100644 index 000000000..ea711596f --- /dev/null +++ b/packages/ui/certd-client/src/api/modules/api.user.ts @@ -0,0 +1,51 @@ +import { request, requestForMock } from "../service"; +import { env } from "/@/utils/util.env"; +/** + * @description: Login interface parameters + */ +export interface LoginReq { + username: string; + password: string; +} + +export interface UserInfoRes { + id: string | number; + username: string; + nickName: string; +} + +export interface LoginRes { + token: string; + expire: number; +} + +export async function login(data: LoginReq): Promise { + if (env.PM_ENABLED === "false") { + //没有开启权限模块,模拟登录 + return await requestForMock({ + url: "/login", + method: "post", + data + }); + } + //如果开启了登录与权限模块,则真实登录 + return await request({ + url: "/login", + method: "post", + data + }); +} + +export async function mine(): Promise { + if (env.PM_ENABLED === "false") { + //没有开启权限模块,模拟登录 + return await requestForMock({ + url: "/sys/authority/user/mine", + method: "post" + }); + } + return await request({ + url: "/sys/authority/user/mine", + method: "post" + }); +} diff --git a/packages/ui/certd-client/src/api/service.ts b/packages/ui/certd-client/src/api/service.ts new file mode 100644 index 000000000..cd8da796f --- /dev/null +++ b/packages/ui/certd-client/src/api/service.ts @@ -0,0 +1,138 @@ +import axios from "axios"; +import { get } from "lodash-es"; +import Adapter from "axios-mock-adapter"; +import { errorLog, errorCreate } from "./tools"; +import { env } from "/src/utils/util.env"; +import { useUserStore } from "../store/modules/user"; +/** + * @description 创建请求实例 + */ +function createService() { + // 创建一个 axios 实例 + const service = axios.create(); + // 请求拦截 + service.interceptors.request.use( + (config) => config, + (error) => { + // 发送失败 + console.log(error); + return Promise.reject(error); + } + ); + // 响应拦截 + service.interceptors.response.use( + (response) => { + if (response.config.responseType === "blob") { + return response; + } + // dataAxios 是 axios 返回数据中的 data + const dataAxios = response.data; + // 这个状态码是和后端约定的 + const { code } = dataAxios; + // 根据 code 进行判断 + if (code === undefined) { + // 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本 + errorCreate(`非标准返回:${dataAxios}, ${response.config.url}`); + return dataAxios; + } else { + // 有 code 代表这是一个后端接口 可以进行进一步的判断 + switch (code) { + case 0: + // [ 示例 ] code === 0 代表没有错误 + // @ts-ignore + if (response.config.unpack === false) { + //如果不需要解包 + return dataAxios; + } + return dataAxios.data; + default: + // 不是正确的 code + errorCreate(`${dataAxios.msg}: ${response.config.url}`); + return dataAxios; + } + } + }, + (error) => { + const status = get(error, "response.status"); + switch (status) { + case 400: + error.message = "请求错误"; + break; + case 401: + error.message = "未授权,请登录"; + break; + case 403: + error.message = "拒绝访问"; + break; + case 404: + error.message = `请求地址出错: ${error.response.config.url}`; + break; + case 408: + error.message = "请求超时"; + break; + case 500: + error.message = "服务器内部错误"; + break; + case 501: + error.message = "服务未实现"; + break; + case 502: + error.message = "网关错误"; + break; + case 503: + error.message = "服务不可用"; + break; + case 504: + error.message = "网关超时"; + break; + case 505: + error.message = "HTTP版本不受支持"; + break; + default: + break; + } + errorLog(error); + if (status === 401) { + const userStore = useUserStore(); + userStore.logout(); + } + return Promise.reject(error); + } + ); + return service; +} + +/** + * @description 创建请求方法 + * @param {Object} service axios 实例 + */ +function createRequestFunction(service) { + return function (config) { + const configDefault = { + headers: { + "Content-Type": get(config, "headers.Content-Type", "application/json") + }, + timeout: 5000, + baseURL: env.API, + data: {} + }; + const userStore = useUserStore(); + const token = userStore.getToken; + if (token != null) { + // @ts-ignore + configDefault.headers.Authorization = token; + } + return service(Object.assign(configDefault, config)); + }; +} + +// 用于真实网络请求的实例和请求方法 +export const service = createService(); +export const request = createRequestFunction(service); + +// 用于模拟网络请求的实例和请求方法 +export const serviceForMock = createService(); +export const requestForMock = createRequestFunction(serviceForMock); + +// 网络请求数据模拟工具 +export const mock = new Adapter(serviceForMock, { delayResponse: 200 }); diff --git a/packages/ui/certd-client/src/api/tools.ts b/packages/ui/certd-client/src/api/tools.ts new file mode 100644 index 000000000..80fa784bf --- /dev/null +++ b/packages/ui/certd-client/src/api/tools.ts @@ -0,0 +1,66 @@ +/** + * @description 安全地解析 json 字符串 + * @param {String} jsonString 需要解析的 json 字符串 + * @param {String} defaultValue 默认值 + */ +import { uiContext } from "@fast-crud/fast-crud"; + +export function parse(jsonString = "{}", defaultValue = {}) { + let result = defaultValue; + try { + result = JSON.parse(jsonString); + } catch (error) { + console.log(error); + } + return result; +} + +/** + * @description 接口请求返回 + * @param {Any} data 返回值 + * @param {String} msg 状态信息 + * @param {Number} code 状态码 + */ +export function response(data = {}, msg = "", code = 0) { + return [200, { code, msg, data }]; +} + +/** + * @description 接口请求返回 正确返回 + * @param {Any} data 返回值 + * @param {String} msg 状态信息 + */ +export function responseSuccess(data = {}, msg = "成功") { + return response(data, msg); +} + +/** + * @description 接口请求返回 错误返回 + * @param {Any} data 返回值 + * @param {String} msg 状态信息 + * @param {Number} code 状态码 + */ +export function responseError(data = {}, msg = "请求失败", code = 500) { + return response(data, msg, code); +} + +/** + * @description 记录和显示错误 + * @param {Error} error 错误对象 + */ +export function errorLog(error) { + // 打印到控制台 + console.error(error); + // 显示提示 + uiContext.get().notification.error({ message: error.message }); +} + +/** + * @description 创建一个错误 + * @param {String} msg 错误信息 + */ +export function errorCreate(msg) { + const error = new Error(msg); + errorLog(error); + throw error; +} diff --git a/packages/ui/certd-client/src/assets/background.svg b/packages/ui/certd-client/src/assets/background.svg new file mode 100644 index 000000000..89c259764 --- /dev/null +++ b/packages/ui/certd-client/src/assets/background.svg @@ -0,0 +1,69 @@ + + + + Group 21 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/ui/certd-client/src/assets/logo.png b/packages/ui/certd-client/src/assets/logo.png new file mode 100644 index 000000000..f3d2503fc Binary files /dev/null and b/packages/ui/certd-client/src/assets/logo.png differ diff --git a/packages/ui/certd-client/src/components/container.vue b/packages/ui/certd-client/src/components/container.vue new file mode 100644 index 000000000..dd92b4545 --- /dev/null +++ b/packages/ui/certd-client/src/components/container.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/packages/ui/certd-client/src/components/dns-provider-selector/api.ts b/packages/ui/certd-client/src/components/dns-provider-selector/api.ts new file mode 100644 index 000000000..4f6f98e7a --- /dev/null +++ b/packages/ui/certd-client/src/components/dns-provider-selector/api.ts @@ -0,0 +1,10 @@ +import { request } from "/src/api/service"; + +const apiPrefix = "/pi/dnsProvider"; + +export async function GetList() { + return await request({ + url: apiPrefix + "/list", + method: "post" + }); +} diff --git a/packages/ui/certd-client/src/components/dns-provider-selector/index.vue b/packages/ui/certd-client/src/components/dns-provider-selector/index.vue new file mode 100644 index 000000000..855c53360 --- /dev/null +++ b/packages/ui/certd-client/src/components/dns-provider-selector/index.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/packages/ui/certd-client/src/components/editable.vue b/packages/ui/certd-client/src/components/editable.vue new file mode 100644 index 000000000..2c090322a --- /dev/null +++ b/packages/ui/certd-client/src/components/editable.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/packages/ui/certd-client/src/components/index.ts b/packages/ui/certd-client/src/components/index.ts new file mode 100644 index 000000000..7992c5e7e --- /dev/null +++ b/packages/ui/certd-client/src/components/index.ts @@ -0,0 +1,18 @@ +import PiContainer from "./container.vue"; +import PiAccessSelector from "../views/certd/access/access-selector/index.vue"; +import PiDnsProviderSelector from "./dns-provider-selector/index.vue"; +import PiOutputSelector from "../views/certd/pipeline/pipeline/component/output-selector/index.vue";import PiEditable from "./editable.vue"; +import { CheckCircleOutlined, InfoCircleOutlined, UndoOutlined } from "@ant-design/icons-vue"; +export default { + install(app) { + app.component("PiContainer", PiContainer); + app.component("PiAccessSelector", PiAccessSelector); + app.component("PiEditable", PiEditable); + app.component("PiOutputSelector", PiOutputSelector); + app.component("PiDnsProviderSelector", PiDnsProviderSelector); + + app.component("CheckCircleOutlined", CheckCircleOutlined); + app.component("InfoCircleOutlined", InfoCircleOutlined); + app.component("UndoOutlined", UndoOutlined); + } +}; diff --git a/packages/ui/certd-client/src/i18n/index.ts b/packages/ui/certd-client/src/i18n/index.ts new file mode 100644 index 000000000..2d432976d --- /dev/null +++ b/packages/ui/certd-client/src/i18n/index.ts @@ -0,0 +1,30 @@ +// @ts-ignore +import { createI18n } from "vue-i18n"; +// +// @ts-ignore +import enFsLocale from "@fast-crud/fast-crud/dist/locale/lang/en.js"; +// @ts-ignore +import zhFsLocale from "@fast-crud/fast-crud/dist/locale/lang/zh-cn.js"; +import en from "./locale/en"; +import zh from "./locale/zh_CN"; +const messages = { + en: { + label: "English", + // 定义您自己的字典,但是请不要和 `fs` 重复,这样会导致 fast-crud 内部组件的翻译失效. + fs: enFsLocale.fs, + ...en + }, + "zh-cn": { + label: "简体中文", + // 定义您自己的字典,但是请不要和 `fs` 重复,这样会导致 fast-crud 内部组件的翻译失效. + fs: zhFsLocale.fs, + ...zh + } +}; + +export default createI18n({ + legacy: false, + locale: "zh-cn", + fallbackLocale: "zh-cn", + messages +}); diff --git a/packages/ui/certd-client/src/i18n/locale/en.ts b/packages/ui/certd-client/src/i18n/locale/en.ts new file mode 100644 index 000000000..d59a415d1 --- /dev/null +++ b/packages/ui/certd-client/src/i18n/locale/en.ts @@ -0,0 +1,3 @@ +export default { + app: { crud: { i18n: { name: "name", city: "city", status: "status" } } } +}; diff --git a/packages/ui/certd-client/src/i18n/locale/zh_CN.ts b/packages/ui/certd-client/src/i18n/locale/zh_CN.ts new file mode 100644 index 000000000..cfb247c36 --- /dev/null +++ b/packages/ui/certd-client/src/i18n/locale/zh_CN.ts @@ -0,0 +1,9 @@ +export default { + app: { + crud: { i18n: { name: "姓名", city: "城市", status: "状态" } }, + login: { + logoutTip: "确认", + logoutMessage: "确定要注销登录吗?" + } + } +}; diff --git a/packages/ui/certd-client/src/layout/components/contextmenu/components/contentmenuList/index.vue b/packages/ui/certd-client/src/layout/components/contextmenu/components/contentmenuList/index.vue new file mode 100644 index 000000000..6e3866f7a --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/contextmenu/components/contentmenuList/index.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/packages/ui/certd-client/src/layout/components/contextmenu/index.vue b/packages/ui/certd-client/src/layout/components/contextmenu/index.vue new file mode 100644 index 000000000..664f41328 --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/contextmenu/index.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/packages/ui/certd-client/src/layout/components/locale/index.vue b/packages/ui/certd-client/src/layout/components/locale/index.vue new file mode 100644 index 000000000..87f8d92ac --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/locale/index.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/packages/ui/certd-client/src/layout/components/menu/index.jsx b/packages/ui/certd-client/src/layout/components/menu/index.jsx new file mode 100644 index 000000000..cb2cb58dd --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/menu/index.jsx @@ -0,0 +1,224 @@ +import { useRoute, useRouter } from "vue-router"; +import { ref, watch, onMounted, onUnmounted, resolveComponent, nextTick, defineComponent } from "vue"; +import getEachDeep from "deepdash-es/getEachDeep"; +import _ from "lodash-es"; +import BScroll from "better-scroll"; +import "./index.less"; +const eachDeep = getEachDeep(_); + +function useBetterScroll(enabled = true) { + let bsRef = ref(null); + let asideMenuRef = ref(); + + let onOpenChange = () => {}; + if (enabled) { + function bsInit() { + if (asideMenuRef.value == null) { + return; + } + bsRef.value = new BScroll(asideMenuRef.value, { + mouseWheel: true, + click: true, + momentum: false, + // 如果你愿意可以打开显示滚动条 + scrollbar: { + fade: true, + interactive: false + }, + bounce: false + }); + } + + function bsDestroy() { + if (bsRef.value != null && bsRef.value.destroy) { + try { + bsRef.value.destroy(); + } catch (e) { + // console.error(e); + } finally { + bsRef.value = null; + } + } + } + + onMounted(() => { + bsInit(); + }); + + onUnmounted(() => { + bsDestroy(); + }); + onOpenChange = async () => { + console.log("onOpenChange"); + setTimeout(() => { + bsRef.value?.refresh(); + }, 300); + }; + } + return { + onOpenChange, + asideMenuRef + }; +} +export default defineComponent({ + name: "FsMenu", + inheritAttrs: true, + props: { + menus: {}, + expandSelected: { + default: false + }, + scroll: {} + }, + setup(props, ctx) { + async function open(path) { + if (path == null) { + return; + } + if (path.startsWith("http://") || path.startsWith("https://")) { + window.open(path); + return; + } + try { + const navigationResult = await router.push(path); + if (navigationResult) { + // 导航被阻止 + } else { + // 导航成功 (包括重新导航的情况) + } + } catch (e) { + console.error("导航失败", e); + } + } + function onSelect(item) { + open(item.key); + } + + const FsIcon = resolveComponent("FsIcon"); + + const buildMenus = (children) => { + const slots = []; + if (children == null) { + return slots; + } + for (let sub of children) { + const title = () => { + if (sub?.meta?.icon) { + return ( +
+ + {sub.title} +
+ ); + } + return sub.title; + }; + if (sub.children && sub.children.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const subSlots = { + default: () => { + return buildMenus(sub.children); + }, + title + }; + function onTitleClick() { + if (sub.path && ctx.attrs.mode === "horizontal") { + open(sub.path); + } + } + slots.push(); + } else { + slots.push( + + {title} + + ); + } + } + return slots; + }; + const slots = { + default() { + return buildMenus(props.menus); + } + }; + const selectedKeys = ref([]); + const openKeys = ref([]); + const route = useRoute(); + const router = useRouter(); + + function openSelectedParents(fullPath) { + if (!props.expandSelected) { + return; + } + if (props.menus == null) { + return; + } + const keys = []; + let changed = false; + eachDeep(props.menus, (value, key, parent, context) => { + if (value == null) { + return; + } + if (value.path === fullPath) { + _.forEach(context.parents, (item) => { + if (item.value instanceof Array) { + return; + } + keys.push(item.value.index); + }); + } + }); + if (keys.length > 0) { + for (let key of keys) { + if (openKeys.value.indexOf(key) === -1) { + openKeys.value.push(key); + changed = true; + } + } + } + return changed; + } + + const { asideMenuRef, onOpenChange } = useBetterScroll(props.scroll); + + watch( + () => { + return route.fullPath; + }, + (path) => { + // path = route.fullPath; + selectedKeys.value = [path]; + const changed = openSelectedParents(path); + if (changed) { + onOpenChange(); + } + }, + { + immediate: true + } + ); + return () => { + const menu = ( + + ); + const classNames = { "fs-menu-wrapper": true, "fs-menu-better-scroll": props.scroll }; + return ( +
+ {menu} +
+ ); + }; + } +}); diff --git a/packages/ui/certd-client/src/layout/components/menu/index.less b/packages/ui/certd-client/src/layout/components/menu/index.less new file mode 100644 index 000000000..9521a4070 --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/menu/index.less @@ -0,0 +1,11 @@ +.fs-menu-wrapper{ + height: 100%; + overflow-y: auto; + &.fs-menu-better-scroll{ + overflow-y: hidden; + } + .menu-item-title{ + display: flex; + align-items: center; + } +} diff --git a/packages/ui/certd-client/src/layout/components/source-link/index.vue b/packages/ui/certd-client/src/layout/components/source-link/index.vue new file mode 100644 index 000000000..9d16a5021 --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/source-link/index.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/packages/ui/certd-client/src/layout/components/tabs/index.vue b/packages/ui/certd-client/src/layout/components/tabs/index.vue new file mode 100644 index 000000000..21ddddb8c --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/tabs/index.vue @@ -0,0 +1,295 @@ + + + + diff --git a/packages/ui/certd-client/src/layout/components/theme/color-picker.vue b/packages/ui/certd-client/src/layout/components/theme/color-picker.vue new file mode 100644 index 000000000..599baf34e --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/theme/color-picker.vue @@ -0,0 +1,101 @@ + + + + diff --git a/packages/ui/certd-client/src/layout/components/theme/index.vue b/packages/ui/certd-client/src/layout/components/theme/index.vue new file mode 100644 index 000000000..bc8a92c05 --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/theme/index.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/packages/ui/certd-client/src/layout/components/user-info/index.vue b/packages/ui/certd-client/src/layout/components/user-info/index.vue new file mode 100644 index 000000000..24af19850 --- /dev/null +++ b/packages/ui/certd-client/src/layout/components/user-info/index.vue @@ -0,0 +1,40 @@ + + diff --git a/packages/ui/certd-client/src/layout/layout-framework.vue b/packages/ui/certd-client/src/layout/layout-framework.vue new file mode 100644 index 000000000..0e57eab86 --- /dev/null +++ b/packages/ui/certd-client/src/layout/layout-framework.vue @@ -0,0 +1,230 @@ + + + + diff --git a/packages/ui/certd-client/src/layout/layout-outside.vue b/packages/ui/certd-client/src/layout/layout-outside.vue new file mode 100644 index 000000000..a35b8c5e1 --- /dev/null +++ b/packages/ui/certd-client/src/layout/layout-outside.vue @@ -0,0 +1,154 @@ + + + + diff --git a/packages/ui/certd-client/src/layout/layout-pass.vue b/packages/ui/certd-client/src/layout/layout-pass.vue new file mode 100644 index 000000000..81c8aa3f3 --- /dev/null +++ b/packages/ui/certd-client/src/layout/layout-pass.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/packages/ui/certd-client/src/main.ts b/packages/ui/certd-client/src/main.ts new file mode 100644 index 000000000..776522909 --- /dev/null +++ b/packages/ui/certd-client/src/main.ts @@ -0,0 +1,23 @@ +import { createApp } from "vue"; +import App from "./App.vue"; +import router from "./router"; +import Antd from "ant-design-vue"; +import "ant-design-vue/dist/antd.less"; +// import "virtual:windi.css"; +import "./style/common.less"; +import "./mock"; +import i18n from "./i18n"; +import store from "./store"; + +import plugin from "./plugin/"; +import components from "./components"; +// @ts-ignore +const app = createApp(App); +// 尽量 +app.use(Antd); +app.use(router); +app.use(i18n); +app.use(store); +app.use(plugin, { i18n }); +app.use(components); +app.mount("#app"); diff --git a/packages/ui/certd-client/src/mock/base.js b/packages/ui/certd-client/src/mock/base.js new file mode 100644 index 000000000..1cacbac71 --- /dev/null +++ b/packages/ui/certd-client/src/mock/base.js @@ -0,0 +1,280 @@ +import _ from "lodash-es"; +function copyList(originList, newList, options, parentId) { + for (const item of originList) { + const newItem = { ...item, parentId }; + newItem.id = ++options.idGenerator; + newList.push(newItem); + if (item.children != null) { + newItem.children = []; + copyList(item.children, newItem.children, options, newItem.id); + } + } +} + +function delById(req, list) { + for (let i = 0; i < list.length; i++) { + const item = list[i]; + console.log("remove i", i, req, req.params.id, item.id); + if (item.id === parseInt(req.params.id)) { + console.log("remove i", i); + list.splice(i, 1); + break; + } + if (item.children != null && item.children.length > 0) { + delById(req, item.children); + } + } +} + +function findById(id, list) { + for (const item of list) { + if (item.id === id) { + return item; + } + if (item.children != null && item.children.length > 0) { + const sub = findById(id, item.children); + if (sub != null) { + return sub; + } + } + } +} +export default { + findById, + buildMock(options) { + const name = options.name; + if (options.copyTimes == null) { + options.copyTimes = 29; + } + const list = []; + for (let i = 0; i < options.copyTimes; i++) { + copyList(options.list, list, options); + } + options.list = list; + return [ + { + path: "/mock/" + name + "/page", + method: "get", + handle(req) { + let data = [...list]; + let limit = 20; + let offset = 0; + for (const item of list) { + if (item.children != null && item.children.length === 0) { + item.hasChildren = false; + item.lazy = false; + } + } + let orderProp, orderAsc; + if (req && req.body) { + const { page, query, sort } = req.body; + if (page.limit != null) { + limit = parseInt(page.limit); + } + if (page.offset != null) { + offset = parseInt(page.offset); + } + orderProp = sort.prop; + orderAsc = sort.asc; + + if (Object.keys(query).length > 0) { + data = list.filter((item) => { + let allFound = true; // 是否所有条件都符合 + for (const key in query) { + // 判定某一个条件 + const value = query[key]; + if (value == null || value === "") { + continue; + } + if (value instanceof Array) { + // 如果条件中的value是数组的话,只要查到一个就行 + if (value.length === 0) { + continue; + } + let found = false; + for (const i of value) { + if (item[key] instanceof Array) { + for (const j of item[key]) { + if (i === j) { + found = true; + break; + } + } + if (found) { + break; + } + } else if (item[key] === i || (typeof item[key] === "string" && item[key].indexOf(i + "") >= 0)) { + found = true; + break; + } + if (found) { + break; + } + } + if (!found) { + allFound = false; + } + } else if (value instanceof Object) { + for (const key2 in value) { + const v = value[key2]; + if (v && item[key] && v !== item[key][key2]) { + return false; + } + } + } else if (item[key] !== value) { + allFound = false; + } + } + return allFound; + }); + } + } + + const start = offset; + let end = offset + limit; + if (data.length < end) { + end = data.length; + } + + if (orderProp) { + // 排序 + data.sort((a, b) => { + let ret = 0; + if (a[orderProp] > b[orderProp]) { + ret = 1; + } else { + ret = -1; + } + return orderAsc ? ret : -ret; + }); + } + + const records = data.slice(start, end); + const lastOffset = data.length - (data.length % limit); + if (offset > lastOffset) { + offset = lastOffset; + } + return { + code: 0, + msg: "success", + data: { + records: records, + total: data.length, + limit, + offset + } + }; + } + }, + { + path: "/mock/" + name + "/get", + method: "get", + handle(req) { + let id = req.params.id; + id = parseInt(id); + let current = null; + for (const item of list) { + if (item.id === id) { + current = item; + break; + } + } + return { + code: 0, + msg: "success", + data: current + }; + } + }, + { + path: "/mock/" + name + "/add", + method: "post", + handle(req) { + req.body.id = ++options.idGenerator; + list.unshift(req.body); + return { + code: 0, + msg: "success", + data: req.body.id + }; + } + }, + { + path: "/mock/" + name + "/update", + method: "post", + handle(req) { + const item = findById(req.body.id, list); + if (item) { + _.mergeWith(item, req.body, (objValue, srcValue) => { + if (srcValue == null) { + return; + } + // 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空 + if (_.isArray(objValue)) { + return srcValue; + } + }); + } + return { + code: 0, + msg: "success", + data: null + }; + } + }, + { + path: "/mock/" + name + "/delete", + method: "post", + handle(req) { + delById(req, list); + return { + code: 0, + msg: "success", + data: null + }; + } + }, + { + path: "/mock/" + name + "/batchDelete", + method: "post", + handle(req) { + const ids = req.body.ids; + for (let i = list.length - 1; i >= 0; i--) { + const item = list[i]; + if (ids.indexOf(item.id) >= 0) { + list.splice(i, 1); + } + } + return { + code: 0, + msg: "success", + data: null + }; + } + }, + { + path: "/mock/" + name + "/delete", + method: "post", + handle(req) { + delById(req, list); + return { + code: 0, + msg: "success", + data: null + }; + } + }, + { + path: "/mock/" + name + "/all", + method: "post", + handle(req) { + return { + code: 0, + msg: "success", + data: list + }; + } + } + ]; + } +}; diff --git a/packages/ui/certd-client/src/mock/common/cascader-data.js b/packages/ui/certd-client/src/mock/common/cascader-data.js new file mode 100644 index 000000000..2b22016ff --- /dev/null +++ b/packages/ui/certd-client/src/mock/common/cascader-data.js @@ -0,0 +1,268 @@ +export default [ + { + value: "zhinan", + label: "指南", + children: [ + { + value: "shejiyuanze", + label: "设计原则", + children: [ + { + value: "yizhi", + label: "一致" + }, + { + value: "fankui", + label: "反馈" + }, + { + value: "xiaolv", + label: "效率" + }, + { + value: "kekong", + label: "可控" + } + ] + }, + { + value: "daohang", + label: "导航", + children: [ + { + value: "cexiangdaohang", + label: "侧向导航" + }, + { + value: "dingbudaohang", + label: "顶部导航" + } + ] + } + ] + }, + { + value: "zujian", + label: "组件", + children: [ + { + value: "basic", + label: "Basic", + children: [ + { + value: "layout", + label: "Layout 布局" + }, + { + value: "color", + label: "Color 色彩" + }, + { + value: "typography", + label: "Typography 字体" + }, + { + value: "icon", + label: "Icon 图标" + }, + { + value: "button", + label: "Button 按钮" + } + ] + }, + { + value: "form", + label: "Form", + children: [ + { + value: "radio", + label: "Radio 单选框" + }, + { + value: "checkbox", + label: "Checkbox 多选框" + }, + { + value: "input", + label: "Input 输入框" + }, + { + value: "input-number", + label: "InputNumber 计数器" + }, + { + value: "select", + label: "Select 选择器" + }, + { + value: "cascader", + label: "Cascader 级联选择器" + }, + { + value: "switch", + label: "Switch 开关" + }, + { + value: "slider", + label: "Slider 滑块" + }, + { + value: "time-picker", + label: "TimePicker 时间选择器" + }, + { + value: "date-picker", + label: "DatePicker 日期选择器" + }, + { + value: "datetime-picker", + label: "DateTimePicker 日期时间选择器" + }, + { + value: "upload", + label: "Upload 上传" + }, + { + value: "rate", + label: "Rate 评分" + }, + { + value: "form1", + label: "Form 表单" + } + ] + }, + { + value: "data", + label: "Data", + children: [ + { + value: "table", + label: "Table 表格" + }, + { + value: "tag", + label: "Tag 标签" + }, + { + value: "progress", + label: "Progress 进度条" + }, + { + value: "tree", + label: "Tree 树形控件" + }, + { + value: "pagination", + label: "Pagination 分页" + }, + { + value: "badge", + label: "Badge 标记" + } + ] + }, + { + value: "notice", + label: "Notice", + children: [ + { + value: "alert", + label: "Alert 警告" + }, + { + value: "loading", + label: "Loading 加载" + }, + { + value: "message", + label: "Message 消息提示" + }, + { + value: "message-box", + label: "MessageBox 弹框" + }, + { + value: "notification", + label: "Notification 通知" + } + ] + }, + { + value: "navigation", + label: "Navigation", + children: [ + { + value: "menu", + label: "NavMenu 导航菜单" + }, + { + value: "tabs", + label: "Tabs 标签页" + }, + { + value: "breadcrumb", + label: "Breadcrumb 面包屑" + }, + { + value: "dropdown", + label: "Dropdown 下拉菜单" + }, + { + value: "steps", + label: "Steps 步骤条" + } + ] + }, + { + value: "others", + label: "Others", + children: [ + { + value: "dialog", + label: "Dialog 对话框" + }, + { + value: "tooltip", + label: "Tooltip 文字提示" + }, + { + value: "popover", + label: "Popover 弹出框" + }, + { + value: "card", + label: "Card 卡片" + }, + { + value: "carousel", + label: "Carousel 走马灯" + }, + { + value: "collapse", + label: "Collapse 折叠面板" + } + ] + } + ] + }, + { + value: "ziyuan", + label: "资源", + children: [ + { + value: "axure", + label: "Axure Components" + }, + { + value: "sketch", + label: "Sketch Templates" + }, + { + value: "jiaohu", + label: "组件交互文档" + } + ] + } +]; diff --git a/packages/ui/certd-client/src/mock/common/mock.dict.js b/packages/ui/certd-client/src/mock/common/mock.dict.js new file mode 100644 index 000000000..d758ddd4e --- /dev/null +++ b/packages/ui/certd-client/src/mock/common/mock.dict.js @@ -0,0 +1,123 @@ +import cascaderData from "./cascader-data"; +import pcaDataLittle from "./pca-data-little"; +import { TreeNodesLazyLoader, getPcaData } from "./pcas-data"; + +const openStatus = [ + { value: "1", label: "打开", color: "success",icon:"ion:radio-button-on" }, + { value: "2", label: "停止", color: "cyan" }, + { value: "0", label: "关闭", color: "red",icon:"ion:radio-button-off" } +]; + +const moreOpenStatus = [ + { value: "1", label: "打开(open)", color: "success" }, + { value: "2", label: "停止(stop)", color: "cyan" }, + { value: "0", label: "关闭(close)", color: "red" } +]; + +const textStatus = [ + { id: "1", text: "打开", color: "success" }, + { id: "2", text: "停止", color: "cyan" }, + { id: "0", text: "关闭", color: "red" } +]; + +export function GetTreeChildrenByParentId(parentId) { + return TreeNodesLazyLoader.getChildren(parentId); +} + +export function GetNodesByValues(values) { + return TreeNodesLazyLoader.getNodesByValues(values); +} + +export default [ + { + path: "/mock/dicts/OpenStatusEnum", + method: "get", + handle() { + return { + code: 0, + msg: "success", + data: openStatus + }; + } + }, + { + path: "/mock/dicts/_OpenStatusEnum2", + method: "get", + handle() { + return { + code: 0, + msg: "success", + data: textStatus + }; + } + }, + { + path: "/mock/dicts/moreOpenStatusEnum", + method: "get", + handle() { + return { + code: 0, + msg: "success", + data: moreOpenStatus + }; + } + }, + { + path: "/mock/dicts/cascaderData", + method: "get", + handle() { + return { + code: 0, + msg: "success", + data: cascaderData + }; + } + }, + { + path: "/mock/dicts/pca", + method: "get", + async handle() { + const data = await getPcaData(); + return { + code: 0, + msg: "success", + data: data + }; + } + }, + { + path: "/mock/dicts/littlePca", + method: "get", + async handle() { + return { + code: 0, + msg: "success", + data: pcaDataLittle + }; + } + }, + { + path: "/mock/tree/GetTreeChildrenByParentId", + method: "get", + async handle({ params }) { + const list = await GetTreeChildrenByParentId(params.parentId); + return { + code: 0, + msg: "success", + data: list + }; + } + }, + { + path: "/mock/tree/GetNodesByValues", + method: "get", + async handle({ params }) { + const list = await GetNodesByValues(params.values); + return { + code: 0, + msg: "success", + data: list + }; + } + } +]; diff --git a/packages/ui/certd-client/src/mock/common/pca-data-little.js b/packages/ui/certd-client/src/mock/common/pca-data-little.js new file mode 100644 index 000000000..ae5aa367a --- /dev/null +++ b/packages/ui/certd-client/src/mock/common/pca-data-little.js @@ -0,0 +1,70 @@ +export default [ + { + code: "1", + name: "北京", + children: [ + { + code: "2", + name: "北京市区", + children: [ + { + code: "3", + name: "海淀" + }, + { + code: "4", + name: "朝阳" + } + ] + }, + { + code: "5", + name: "北京郊区", + children: [ + { + code: "6", + name: "海淀郊区" + }, + { + code: "7", + name: "朝阳郊区" + } + ] + } + ] + }, + { + code: "11", + name: "深圳", + children: [ + { + code: "12", + name: "深圳市区", + children: [ + { + code: "13", + name: "南山" + }, + { + code: "14", + name: "福田" + } + ] + }, + { + code: "15", + name: "深圳郊区", + children: [ + { + code: "16", + name: "南山郊区" + }, + { + code: "17", + name: "福田郊区" + } + ] + } + ] + } +]; diff --git a/packages/ui/certd-client/src/mock/common/pcas-data.js b/packages/ui/certd-client/src/mock/common/pcas-data.js new file mode 100644 index 000000000..c1bbb64a7 --- /dev/null +++ b/packages/ui/certd-client/src/mock/common/pcas-data.js @@ -0,0 +1,87 @@ +import _ from "lodash-es"; +export async function getPcasData() { + const pcasData = () => import("china-division/dist/pcas-code.json"); + const ret = await pcasData(); + return ret.default; +} +export async function getPcaData() { + const pcaData = () => import("china-division/dist/pca-code.json"); + const ret = await pcaData(); + return ret.default; +} +export const TreeNodesLazyLoader = { + getNodesByValues(values) { + console.log("getNodesByValues", values); + if (!(values instanceof Array)) { + values = [values]; + } + return getPcasData().then((data) => { + const nodes = []; + for (const value of values) { + const found = this.getNode(data, value); + if (found) { + const target = _.cloneDeep(found); + delete target.children; + nodes.push(target); + } + } + return nodes; + }); + }, + getNode(list, value) { + for (const item of list) { + if (item.code === value) { + return item; + } + if (item.children && item.children.length > 0) { + const found = this.getNode(item.children, value); + if (found) { + return found; + } + } + } + }, + getChildren(parent) { + return getPcasData().then((data) => { + const list = this.getChildrenByParent(parent, data); + if (list == null) { + return []; + } + return this.cloneAndDeleteChildren(list); + }); + }, + getChildrenByParent(parentId, tree) { + if (!parentId) { + // 取第一级 + return tree; + } else { + for (const node of tree) { + if (node.code === parentId) { + return node.children; + } + if (node.children && node.children.length > 0) { + // 递归查找 + const list = this.getChildrenByParent(parentId, node.children); + if (list) { + return list; + } + } + } + } + }, + cloneAndDeleteChildren(list) { + const newList = []; + for (const node of list) { + const newNode = {}; + Object.assign(newNode, node); + if (newNode.children == null || newNode.children.length === 0) { + newNode.isLeaf = true; + newNode.leaf = true; + } + delete newNode.children; + newList.push(newNode); + } + console.log("found children:", newList); + return newList; + } +}; diff --git a/packages/ui/certd-client/src/mock/index.js b/packages/ui/certd-client/src/mock/index.js new file mode 100644 index 000000000..4494fc5f9 --- /dev/null +++ b/packages/ui/certd-client/src/mock/index.js @@ -0,0 +1,49 @@ +import { mock } from "../api/service"; +import * as tools from "../api/tools"; +import _ from "lodash-es"; +const commonMocks = import.meta.globEager("./common/mock.*.js"); +const apiMocks = import.meta.globEager("../api/modules/*.mock.ts"); +const viewMocks = import.meta.globEager("../views/**/mock.js"); + +const list = []; +_.forEach(commonMocks, (value) => { + list.push(value.default); +}); +_.forEach(apiMocks, (value) => { + list.push(value.default); +}); +_.forEach(viewMocks, (value) => { + list.push(value.default); +}); + +list.forEach((apiFile) => { + for (const item of apiFile) { + mock.onAny(new RegExp(item.path)).reply(async (config) => { + console.log("------------fake request start -------------"); + console.log("request:", config); + const data = config.data ? JSON.parse(config.data) : {}; + const query = config.url.indexOf("?") >= 0 ? config.url.substring(config.url.indexOf("?") + 1) : undefined; + const params = config.params || {}; + if (query) { + const arr = query.split("&"); + for (const item of arr) { + const kv = item.split("="); + params[kv[0]] = kv[1]; + } + } + + const req = { + body: data, + params: params + }; + const ret = await item.handle(req); + console.log("response:", ret); + console.log("------------fake request end-------------"); + if (ret.code === 0) { + return tools.responseSuccess(ret.data, ret.msg); + } else { + return tools.responseError(ret.data, ret.msg, ret.code); + } + }); + } +}); diff --git a/packages/ui/certd-client/src/plugin/fast-crud/index.ts b/packages/ui/certd-client/src/plugin/fast-crud/index.ts new file mode 100644 index 000000000..c89c25faf --- /dev/null +++ b/packages/ui/certd-client/src/plugin/fast-crud/index.ts @@ -0,0 +1,225 @@ +import { request, requestForMock } from "/src/api/service"; +import "/src/mock"; +import UiAntdv from "@fast-crud/ui-antdv"; +import { FastCrud, UseCrudProps, useTypes, setLogger } from "@fast-crud/fast-crud"; +import "@fast-crud/fast-crud/dist/style.css"; +import { FsExtendsUploader, FsExtendsEditor, FsExtendsJson, FsExtendsCopyable, FsExtendsTime } from "@fast-crud/fast-extends"; +import "@fast-crud/fast-extends/dist/style.css"; + +import { useCrudPermission } from "../permission"; + +function install(app, options: any = {}) { + app.use(UiAntdv); + //设置日志级别 + setLogger({ level: "debug" }); + app.use(FastCrud, { + i18n: options.i18n, + async dictRequest({ url }) { + if (url && url.startsWith("/mock")) { + //如果是crud开头的dict请求视为mock + return await requestForMock({ url, method: "post" }); + } + return await request({ url, method: "post" }); + }, + /** + * useCrud时会被执行 + * @param context,useCrud的参数 + */ + commonOptions(context: UseCrudProps) { + const crudBinding = context.expose?.crudBinding; + const opts = { + table: { + size: "small", + pagination: false, + onResizeColumn: (w, col) => { + crudBinding.value.table.columnsMap[col.key].width = w; + } + }, + rowHandle: { + buttons: { + view: { type: "link", text: null, icon: "ion:eye-outline" }, + edit: { type: "link", text: null, icon: "ion:create-outline" }, + remove: { type: "link", style: { color: "red" }, text: null, icon: "ion:trash-outline" } + }, + dropdown: { + more: { + type: "link" + } + } + }, + request: { + transformQuery: ({ page, form, sort }) => { + const limit = page.pageSize; + const currentPage = page.currentPage ?? 1; + const offset = limit * (currentPage - 1); + + sort = sort == null ? {} : sort; + + return { + page: { + limit, + offset + }, + query: form, + sort + }; + }, + transformRes: ({ res }) => { + const pageSize = res.limit; + let currentPage = res.offset / pageSize; + if (res.offset % pageSize === 0) { + currentPage++; + } + return { currentPage, pageSize, ...res }; + } + }, + form: { + display: "flex", + labelCol: { + //固定label宽度 + span: null, + style: { + width: "120px" + } + }, + wrapperCol: { + span: null + } + } + }; + + // 从 useCrud({permission}) 里获取permission参数,去设置各个按钮的权限 + const crudPermission = useCrudPermission({ permission: context.permission }); + return crudPermission.merge(opts); + } + }); + + // fast-extends里面的扩展组件均为异步组件,只有在使用时才会被加载,并不会影响首页加载速度 + //安装uploader 公共参数 + app.use(FsExtendsUploader, { + defaultType: "cos", + cos: { + domain: "https://d2p-demo-1251260344.cos.ap-guangzhou.myqcloud.com", + bucket: "d2p-demo-1251260344", + region: "ap-guangzhou", + secretId: "", // + secretKey: "", // 传了secretKey 和secretId 代表使用本地签名模式(不安全,生产环境不推荐) + getAuthorization(custom) { + // 不传secretKey代表使用临时签名模式,此时此参数必传(安全,生产环境推荐) + return request({ + url: "http://www.docmirror.cn:7070/api/upload/cos/getAuthorization", + method: "get" + }).then((ret) => { + // 返回结构如下 + // ret.data:{ + // TmpSecretId, + // TmpSecretKey, + // XCosSecurityToken, + // ExpiredTime, // SDK 在 ExpiredTime 时间前,不会再次调用 getAuthorization + // } + return ret; + }); + }, + successHandle(ret) { + // 上传完成后可以在此处处理结果,修改url什么的 + console.log("success handle:", ret); + return ret; + } + }, + alioss: { + domain: "https://d2p-demo.oss-cn-shenzhen.aliyuncs.com", + bucket: "d2p-demo", + region: "oss-cn-shenzhen", + accessKeyId: "", + accessKeySecret: "", + async getAuthorization(custom, context) { + // 不传accessKeySecret代表使用临时签名模式,此时此参数必传(安全,生产环境推荐) + const ret = await request({ + url: "http://www.docmirror.cn:7070/api/upload/alioss/getAuthorization", + method: "get" + }); + console.log("ret", ret); + return ret; + }, + sdkOpts: { + // sdk配置 + secure: true // 默认为非https上传,为了安全,设置为true + }, + successHandle(ret) { + // 上传完成后可以在此处处理结果,修改url什么的 + console.log("success handle:", ret); + return ret; + } + }, + qiniu: { + bucket: "d2p-demo", + async getToken(options) { + const ret = await request({ + url: "http://www.docmirror.cn:7070/api/upload/qiniu/getToken", + method: "get" + }); + return ret; // {token:xxx,expires:xxx} + }, + successHandle(ret) { + // 上传完成后可以在此处处理结果,修改url什么的 + console.log("success handle:", ret); + return ret; + }, + domain: "http://d2p.file.handsfree.work/" + }, + form: { + action: "http://www.docmirror.cn:7070/api/upload/form/upload", + name: "file", + withCredentials: false, + uploadRequest: async ({ action, file, onProgress }) => { + // @ts-ignore + const data = new FormData(); + data.append("file", file); + return await request({ + url: action, + method: "post", + headers: { + "Content-Type": "multipart/form-data" + }, + timeout: 60000, + data, + onUploadProgress: (p) => { + onProgress({ percent: Math.round((p.loaded / p.total) * 100) }); + } + }); + }, + successHandle(ret) { + // 上传完成后的结果处理, 此处应返回格式为{url:xxx} + return { + url: "http://www.docmirror.cn:7070" + ret, + key: ret.replace("/api/upload/form/download?key=", "") + }; + } + } + }); + + //安装editor + app.use(FsExtendsEditor, { + //编辑器的公共配置 + wangEditor: {} + }); + app.use(FsExtendsJson); + app.use(FsExtendsTime); + app.use(FsExtendsCopyable); + + const { addTypes } = useTypes(); + addTypes({ + time2: { + //如果与官方字段类型同名,将会覆盖官方的字段类型 + form: { component: { name: "a-date-picker" } }, + column: { component: { name: "fs-date-format", format: "YYYY-MM-DD" } }, + valueBuilder(context) { + console.log("time2,valueBuilder", context); + } + } + }); +} + +export default { + install +}; diff --git a/packages/ui/certd-client/src/plugin/iconfont/iconfont.js b/packages/ui/certd-client/src/plugin/iconfont/iconfont.js new file mode 100644 index 000000000..357dc3bdd --- /dev/null +++ b/packages/ui/certd-client/src/plugin/iconfont/iconfont.js @@ -0,0 +1 @@ +!function(t){var e,c,o,n,l,a='',i=(i=document.getElementsByTagName("script"))[i.length-1].getAttribute("data-injectcss"),h=function(t,e){e.parentNode.insertBefore(t,e)};if(i&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}function s(){l||(l=!0,o())}function d(){try{n.documentElement.doScroll("left")}catch(t){return void setTimeout(d,50)}s()}e=function(){var t,e=document.createElement("div");e.innerHTML=a,a=null,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?h(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(e,0):(c=function(){document.removeEventListener("DOMContentLoaded",c,!1),e()},document.addEventListener("DOMContentLoaded",c,!1)):document.attachEvent&&(o=e,n=t.document,l=!1,d(),n.onreadystatechange=function(){"complete"==n.readyState&&(n.onreadystatechange=null,s())})}(window); diff --git a/packages/ui/certd-client/src/plugin/iconfont/index.ts b/packages/ui/certd-client/src/plugin/iconfont/index.ts new file mode 100644 index 000000000..e1f5d7f5b --- /dev/null +++ b/packages/ui/certd-client/src/plugin/iconfont/index.ts @@ -0,0 +1 @@ +import "./iconfont.js" diff --git a/packages/ui/certd-client/src/plugin/iconify/index.ts b/packages/ui/certd-client/src/plugin/iconify/index.ts new file mode 100644 index 000000000..34aacf5ae --- /dev/null +++ b/packages/ui/certd-client/src/plugin/iconify/index.ts @@ -0,0 +1,2 @@ +// import "@iconify/iconify"; +import "@purge-icons/generated"; diff --git a/packages/ui/certd-client/src/plugin/index.ts b/packages/ui/certd-client/src/plugin/index.ts new file mode 100644 index 000000000..4a73280d1 --- /dev/null +++ b/packages/ui/certd-client/src/plugin/index.ts @@ -0,0 +1,12 @@ +import "./iconify"; +import "./iconfont"; +import FastCrud from "./fast-crud"; +import permission from "./permission"; +function install(app, options: any = {}) { + app.use(FastCrud, options); + app.use(permission); +} + +export default { + install +}; diff --git a/packages/ui/certd-client/src/plugin/permission/api.ts b/packages/ui/certd-client/src/plugin/permission/api.ts new file mode 100644 index 000000000..2b9c1ea4c --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/api.ts @@ -0,0 +1,10 @@ +import { request } from "/src/api/service"; +export async function getPermissions() { + const ret = await request({ + url: "/sys/authority/user/permissions", + method: "post" + }); + // 如果使用你自己的后端,需要在此处将返回结果改造为本模块需要的结构 + // 结构详情,请参考示例中打印的日志 ”获取权限数据成功:{...}“ (实际上就是“资源管理”页面中列出来的数据) + return ret; +} diff --git a/packages/ui/certd-client/src/plugin/permission/directive/index.js b/packages/ui/certd-client/src/plugin/permission/directive/index.js new file mode 100644 index 000000000..e0e5181f7 --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/directive/index.js @@ -0,0 +1,9 @@ +import permission from "./permission"; +import permissionUtil from "../util.permission"; +const install = function (app) { + app.directive("permission", permission); + app.config.globalProperties.$hasPermissions = permissionUtil.hasPermissions; +}; + +permission.install = install; +export default permission; diff --git a/packages/ui/certd-client/src/plugin/permission/directive/permission.js b/packages/ui/certd-client/src/plugin/permission/directive/permission.js new file mode 100644 index 000000000..0b2a95786 --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/directive/permission.js @@ -0,0 +1,11 @@ +import permissionUtil from "../util.permission"; +export default { + mounted(el, binding, vnode) { + const { value } = binding; + const hasPermission = permissionUtil.hasPermissions(value); + + if (!hasPermission) { + el.parentNode && el.parentNode.removeChild(el); + } + } +}; diff --git a/packages/ui/certd-client/src/plugin/permission/errors.ts b/packages/ui/certd-client/src/plugin/permission/errors.ts new file mode 100644 index 000000000..6537f4544 --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/errors.ts @@ -0,0 +1,5 @@ +export class NoPermissionError extends Error { + constructor(message?: string) { + super(message || "对不起,您没有权限执行此操作"); + } +} diff --git a/packages/ui/certd-client/src/plugin/permission/hook.ts b/packages/ui/certd-client/src/plugin/permission/hook.ts new file mode 100644 index 000000000..af4b0c76a --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/hook.ts @@ -0,0 +1,45 @@ +import router from "/src/router"; +import { useUserStore } from "/@/store/modules/user"; +import { usePermissionStore } from "./store.permission"; +import util from "./util.permission"; +import { message } from "ant-design-vue"; +import NProgress from "nprogress"; +export function registerRouterHook() { + // 注册路由beforeEach钩子,在第一次加载路由页面时,加载权限 + router.beforeEach(async (to, from, next) => { + const permissionStore = usePermissionStore(); + if (permissionStore.isInited) { + if (to.meta.permission) { + //校验权限 + // @ts-ignore + if (!util.hasPermissions(to.meta.permission)) { + //没有权限 + message.warn("对不起,您没有权限"); + //throw new Error("对不起,您没有权限"); + NProgress.done(); + return false; + } + } + next(); + return; + } + + const userStore = useUserStore(); + const token = userStore.getToken; + if (!token || token === "undefined") { + next(); + return; + } + + // 初始化权限列表 + try { + console.log("permission is enabled"); + await permissionStore.loadFromRemote(); + console.log("PM load success"); + next({ ...to, replace: true }); + } catch (e) { + console.error("加载动态路由失败", e); + next(); + } + }); +} diff --git a/packages/ui/certd-client/src/plugin/permission/index.ts b/packages/ui/certd-client/src/plugin/permission/index.ts new file mode 100644 index 000000000..c300560a2 --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/index.ts @@ -0,0 +1,23 @@ +import permissionDirective from "./directive/index"; +import { registerRouterHook } from "./hook"; +import util from "./util.permission"; +export * from "./use-crud-permission"; +export * from "./errors"; + +export function usePermission() { + return { + ...util + }; +} + +export default { + install(app) { + // 开启权限模块 + // 注册v-permission指令, 用于控制按钮权限 + app.use(permissionDirective); + // 注册路由钩子 + // 通过路由守卫,在登录成功后拦截路由,从后台加载权限数据 + // 然后将权限数据转化为菜单和路由,添加到系统中 + registerRouterHook(); + } +}; diff --git a/packages/ui/certd-client/src/plugin/permission/store.permission.ts b/packages/ui/certd-client/src/plugin/permission/store.permission.ts new file mode 100644 index 000000000..5cbb3305a --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/store.permission.ts @@ -0,0 +1,89 @@ +import { defineStore } from "pinia"; +import { useResourceStore } from "/src/store/modules/resource"; +import { getPermissions } from "./api"; +import { mitter } from "/@/utils/util.mitt"; +import { env } from "/@/utils/util.env"; + +//监听注销事件 +mitter.on("app.logout", () => { + const permissionStore = usePermissionStore(); + permissionStore.clear(); +}); + +interface PermissionState { + permissions: []; + inited: boolean; +} + +/** + * 构建权限码列表 + * @param menuTree + * @param permissionList + * @returns {*} + */ +function formatPermissions(menuTree: Array, permissionList = []) { + if (menuTree == null) { + menuTree = []; + } + menuTree.forEach((item: any) => { + if (item.permission) { + // @ts-ignore + permissionList.push(item.permission); + } + if (item.children != null && item.children.length > 0) { + formatPermissions(item.children, permissionList); + } + }); + return permissionList; +} + +export const usePermissionStore = defineStore({ + id: "app.permission", + state: (): PermissionState => ({ + permissions: [], + inited: false + }), + getters: { + getPermissions() { + // @ts-ignore + return this.permissions; + }, + isInited() { + // @ts-ignore + return this.inited; + } + }, + actions: { + init({ permissions }) { + this.permissions = permissions; + this.inited = true; + }, + clear() { + this.permissions = []; + this.inited = false; + }, + resolve(resourceTree) { + const permissions = formatPermissions(resourceTree); + this.init({ permissions }); + + //过滤没有权限的菜单 + const resourceStore = useResourceStore(); + resourceStore.filterByPermission(permissions); + }, + async loadFromRemote() { + let permissionTree = []; + if (env.PM_ENABLED === "false") { + console.warn("当前权限模块未开启,权限列表为空"); + } else { + //开启了权限模块,向后台请求权限列表 + const data = await getPermissions(); + if (data != null) { + permissionTree = data; + } else { + console.warn("当前获取到的权限列表为空"); + } + } + this.resolve(permissionTree); + } + } +}); diff --git a/packages/ui/certd-client/src/plugin/permission/use-crud-permission.ts b/packages/ui/certd-client/src/plugin/permission/use-crud-permission.ts new file mode 100644 index 000000000..2c60f8e2c --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/use-crud-permission.ts @@ -0,0 +1,60 @@ +import { usePermission } from "/@/plugin/permission"; +import _ from "lodash-es"; + +/** + * 设置按钮动作权限 + * @param permission {prefix,extra} + */ +export function useCrudPermission({ permission }) { + const { hasPermissions } = usePermission(); + + const prefix = permission instanceof Object ? permission.prefix : permission; + + //根据权限显示按钮 + function hasActionPermission(action) { + if (!prefix) { + return true; + } + return hasPermissions(prefix + ":" + action); + } + + function buildCrudPermission() { + if (permission == null) { + return {}; + } + + let extra = {}; + if (permission instanceof Object) { + extra = permission.extra; + if (permission.extra && permission.extra instanceof Function) { + extra = permission.extra({ hasActionPermission }); + } + } + + return _.merge( + { + actionbar: { + buttons: { + add: { show: hasActionPermission("add") } + } + }, + rowHandle: { + buttons: { + edit: { show: hasActionPermission("edit") }, + remove: { show: hasActionPermission("remove") }, + view: { show: hasActionPermission("view") } + } + } + }, + extra + ); + } + + function merge(userOptions) { + const permissionOptions = buildCrudPermission(); + _.merge(permissionOptions, userOptions); + return permissionOptions; + } + + return { merge, buildCrudPermission, hasActionPermission }; +} diff --git a/packages/ui/certd-client/src/plugin/permission/util.permission.ts b/packages/ui/certd-client/src/plugin/permission/util.permission.ts new file mode 100644 index 000000000..1c197335a --- /dev/null +++ b/packages/ui/certd-client/src/plugin/permission/util.permission.ts @@ -0,0 +1,29 @@ +import { usePermissionStore } from "./store.permission"; +import { NoPermissionError } from "./errors"; +import { message } from "ant-design-vue"; +const util = { + hasPermissions: (value: string | string[]): boolean => { + let need: string[] = []; + if (typeof value === "string") { + need.push(value); + } else if (value && value instanceof Array && value.length > 0) { + need = need.concat(value); + } + if (need.length === 0) { + throw new Error('need permissions! Like "sys:user:view" '); + } + const permissionStore = usePermissionStore(); + const userPermissionList = permissionStore.getPermissions; + return userPermissionList.some((permission) => { + return need.includes(permission); + }); + }, + requirePermissions: (value) => { + if (!util.hasPermissions(value)) { + message.error("对不起,您没有权限执行此操作"); + throw new NoPermissionError(); + } + } +}; + +export default util; diff --git a/packages/ui/certd-client/src/router/index.ts b/packages/ui/certd-client/src/router/index.ts new file mode 100644 index 000000000..77a592558 --- /dev/null +++ b/packages/ui/certd-client/src/router/index.ts @@ -0,0 +1,69 @@ +import { createRouter, createWebHashHistory } from "vue-router"; +// 进度条 +import NProgress from "nprogress"; +import "nprogress/nprogress.css"; +import { usePageStore } from "../store/modules/page"; +import { site } from "../utils/util.site"; +import { routes } from "./resolve"; +import { useResourceStore } from "../store/modules/resource"; +import { useUserStore } from "../store/modules/user"; + +const router = createRouter({ + history: createWebHashHistory(), + routes +}); + +/** + * 路由拦截 + */ +router.beforeEach(async (to, from, next) => { + // 进度条 + NProgress.start(); + + // 验证当前路由所有的匹配中是否需要有登录验证的 + if ( + to.matched.some((r) => { + return r.meta?.auth || r.meta?.permission; + }) + ) { + const userStore = useUserStore(); + // 这里暂时将cookie里是否存有token作为验证是否登录的条件 + // 请根据自身业务需要修改 + const token = userStore.getToken; + if (token) { + next(); + } else { + // 没有登录的时候跳转到登录界面 + // 携带上登陆成功之后需要跳转的页面完整路径 + next({ + name: "login", + query: { + redirect: to.fullPath + } + }); + // https://github.com/d2-projects/d2-admin/issues/138 + NProgress.done(); + } + } else { + // 不需要身份校验 直接通过 + next(); + } +}); + +router.afterEach((to) => { + // 进度条 + NProgress.done(); + // 多页控制 打开新的页面 + const pageStore = usePageStore(); + pageStore.open(to); + // 更改标题 + site.title(to.meta.title); + + //修改左侧边栏 + const matched = to.matched; + if (matched.length > 0) { + const resourceStore = useResourceStore(); + resourceStore.setAsideMenuByCurrentRoute(matched); + } +}); +export default router; diff --git a/packages/ui/certd-client/src/router/resolve.ts b/packages/ui/certd-client/src/router/resolve.ts new file mode 100644 index 000000000..39cbd9980 --- /dev/null +++ b/packages/ui/certd-client/src/router/resolve.ts @@ -0,0 +1,155 @@ +import LayoutPass from "/src/layout/layout-pass.vue"; +import _ from "lodash-es"; +import { outsideResource } from "./source/outside"; +import { headerResource } from "./source/header"; +import { frameworkResource } from "./source/framework"; +// @ts-ignore +const modules = import.meta.glob("/src/views/**/*.vue"); + +let index = 0; +function transformOneResource(resource) { + let menu: any = null; + if (resource.meta == null) { + resource.meta = {}; + } + const meta = resource.meta; + meta.title = meta.title ?? resource.title ?? "未命名"; + if (resource.title == null) { + resource.title = meta.title; + } + if (meta.isMenu === false) { + menu = null; + } else { + menu = _.cloneDeep(resource); + delete menu.component; + } + let route; + if (resource.type !== "menu") { + if (resource.path == null || resource.path.startsWith("https://") || resource.path.startsWith("http://")) { + //没有route + route = null; + } else { + route = _.cloneDeep(resource); + if (route.component && typeof route.component === "string") { + const path = "/src/views" + route.component; + route.component = modules[path]; + } + if (route.component == null) { + route.component = LayoutPass; + } + } + } + + return { + menu, + route + }; +} + +export const buildMenusAndRouters = (resources) => { + const routes: Array = []; + const menus: Array = []; + for (const item of resources) { + const { menu, route } = transformOneResource(item); + let menuChildren; + let routeChildren; + if (item.children) { + if (item.children.length > 0) { + const ret = buildMenusAndRouters(item.children); + menuChildren = ret.menus; + routeChildren = ret.routes; + } + } + + if (menu) { + menus.push(menu); + menu.children = menuChildren; + } + if (route) { + if (route?.meta?.cache !== false) { + if (route.meta == null) { + route.meta = {}; + } + route.meta.cache = true; + } + routes.push(route); + route.children = routeChildren; + } + } + + setIndex(menus); + return { + routes, + menus + }; +}; + +function setIndex(menus) { + for (const menu of menus) { + menu.index = "index_" + index; + index++; + if (menu.children && menu.children.length > 0) { + setIndex(menu.children); + } + } +} + +function findMenus(menus, condition) { + const list: any = []; + for (const menu of menus) { + if (condition(menu)) { + list.push(menu); + } + if (menu.children && menu.children.length > 0) { + const subList = findMenus(menu.children, condition); + for (const item of subList) { + list.push(item); + } + } + } + return list; +} + +function filterMenus(menus, condition) { + const list = menus.filter((item) => { + return condition(item); + }); + + for (const item of list) { + if (item.children && item.children.length > 0) { + item.children = filterMenus(item.children, condition); + } + } + return list; +} + +function flatChildren(list, children) { + for (const child of children) { + list.push(child); + if (child.children && child.children.length > 0) { + flatChildren(list, child.children); + } + child.children = null; + } +} +function flatSubRouters(routers) { + for (const router of routers) { + const children: Array = []; + if (router.children && router.children.length > 0) { + flatChildren(children, router.children); + } + router.children = children; + } + return routers; +} + +const frameworkRet = buildMenusAndRouters(frameworkResource); +const outsideRet = buildMenusAndRouters(outsideResource); +const headerRet = buildMenusAndRouters(headerResource); + +const outsideRoutes = outsideRet.routes; +const frameworkRoutes = flatSubRouters(frameworkRet.routes); +const routes = [...outsideRoutes, ...frameworkRoutes]; +const frameworkMenus = frameworkRet.menus; +const headerMenus = headerRet.menus; +export { routes, outsideRoutes, frameworkRoutes, frameworkMenus, headerMenus, findMenus, filterMenus }; diff --git a/packages/ui/certd-client/src/router/source/framework.ts b/packages/ui/certd-client/src/router/source/framework.ts new file mode 100644 index 000000000..800fcf99a --- /dev/null +++ b/packages/ui/certd-client/src/router/source/framework.ts @@ -0,0 +1,35 @@ +import LayoutFramework from "/src/layout/layout-framework.vue"; +//import { crudResources } from "/@/router/source/modules/crud"; +import { sysResources } from "/@/router/source/modules/sys"; +import { certdResources } from "/@/router/source/modules/certd"; + +export const frameworkResource = [ + { + title: "框架", + name: "framework", + path: "/", + redirect: "/index", + component: LayoutFramework, + meta: { + icon: "ion:accessibility", + auth: true + }, + children: [ + { + title: "首页", + name: "index", + path: "/index", + component: "/framework/home/index.vue", + meta: { + fixedAside: true, + showOnHeader: false, + icon: "ion:home-outline" + } + }, + //...crudResources, + ...certdResources, + ...sysResources + ] + } +]; +console.assert(frameworkResource.length === 1, "frameworkResource数组长度只能为1,你只能配置framework路由的子路由"); diff --git a/packages/ui/certd-client/src/router/source/header.ts b/packages/ui/certd-client/src/router/source/header.ts new file mode 100644 index 000000000..8c30304ae --- /dev/null +++ b/packages/ui/certd-client/src/router/source/header.ts @@ -0,0 +1,73 @@ +export const headerResource = [ + { + title: "文档", + path: "http://fast-crud.docmirror.cn/" + }, + { + title: "其他Demo", + name: "demo", + children: [ + { + title: "Element版", + path: "http://fast-crud.docmirror.cn/element/" + }, + { + title: "VbenAdmin", + path: "http://fast-crud.docmirror.cn/vben/" + }, + { + title: "cool-admin-vue", + path: "http://fast-crud.docmirror.cn/cool/" + } + ] + }, + + { + title: "源码", + name: "source", + key: "source", + meta: { + icon: "ion:git-branch-outline" + }, + children: [ + { + title: "fast-crud", + children: [ + { + title: "github", + path: "http://github.com/fast-crud/fast-crud", + meta: { + icon: "ion:logo-github" + } + }, + { + title: "gitee", + path: "http://gitee.com/fast-crud/fast-crud", + meta: { + icon: "ion:logo-octocat" + } + } + ] + }, + { + title: "fs-admin", + children: [ + { + title: "github", + path: "http://github.com/fast-crud/fs-admin-antdv", + meta: { + icon: "ion:logo-github" + } + }, + { + title: "gitee", + path: "http://gitee.com/fast-crud/fs-admin-antdv", + meta: { + icon: "ion:logo-octocat" + } + } + ] + } + ] + } +]; diff --git a/packages/ui/certd-client/src/router/source/modules/certd.ts b/packages/ui/certd-client/src/router/source/modules/certd.ts new file mode 100644 index 000000000..2ece20295 --- /dev/null +++ b/packages/ui/certd-client/src/router/source/modules/certd.ts @@ -0,0 +1,41 @@ +export const certdResources = [ + { + title: "证书自动化", + name: "certd", + path: "/certd", + redirect: "/certd/pipeline", + meta: { + icon: "ion:key-outline", + auth: true + }, + children: [ + { + title: "证书自动化流水线", + name: "pipeline", + path: "/certd/pipeline", + component: "/certd/pipeline/index.vue", + meta: { + icon: "ion:analytics-sharp" + } + }, + { + title: "编辑流水线", + name: "pipelineEdit", + path: "/certd/pipeline/detail", + component: "/certd/pipeline/detail.vue", + meta: { + isMenu: false + } + }, + { + title: "授权管理", + name: "access", + path: "/certd/access", + component: "/certd/access/index.vue", + meta: { + icon: "ion:disc-outline" + } + } + ] + } +]; diff --git a/packages/ui/certd-client/src/router/source/modules/crud.ts b/packages/ui/certd-client/src/router/source/modules/crud.ts new file mode 100644 index 000000000..f17952fc8 --- /dev/null +++ b/packages/ui/certd-client/src/router/source/modules/crud.ts @@ -0,0 +1,604 @@ +export const crudResources = [ + { + title: "CRUD示例", + name: "crud", + path: "/crud", + redirect: "/crud/basis", + meta: { + icon: "ion:apps-sharp" + }, + children: [ + { + title: "基本特性", + name: "basis", + path: "/crud/basis", + redirect: "/crud/basis/i18n", + meta: { + icon: "ion:disc-outline" + }, + children: [ + { + title: "HelloWorld", + name: "FsCrudFirst", + path: "/crud/basis/first", + component: "/crud/basis/first/index.vue" + }, + { + title: "动态计算", + name: "BasisCompute", + path: "/crud/basis/compute", + component: "/crud/basis/compute/index.vue" + }, + { + title: "动态计算-更多示例", + name: "BasisComputeMore", + path: "/crud/basis/compute-more", + component: "/crud/basis/compute-more/index.vue" + }, + { + title: "国际化", + name: "BasisI18n", + path: "/crud/basis/i18n", + component: "/crud/basis/i18n/index.vue" + }, + { + title: "ValueChange", + name: "BasisValueChange", + path: "/crud/basis/value-change", + component: "/crud/basis/value-change/index.vue" + }, + { + title: "Card布局", + name: "BasisLayoutCard", + path: "/crud/basis/layout-card", + component: "/crud/basis/layout-card/index.vue" + }, + { + title: "自定义布局", + name: "BasisLayoutCustom", + path: "/crud/basis/layout-custom", + component: "/crud/basis/layout-custom/index.vue" + }, + { + title: "列设置", + name: "BasisColumnsSet", + path: "/crud/basis/columns-set", + component: "/crud/basis/columns-set/index.vue" + } + ] + }, + { + title: "数据字典", + name: "dict", + path: "/crud/dict", + redirect: "/crud/dict/single", + meta: { + icon: "ion:book-outline" + }, + children: [ + { + title: "单例", + name: "DictSingle", + path: "/crud/dict/single", + component: "/crud/dict/single/index.vue" + }, + { + title: "分发复制", + name: "DictCloneable", + path: "/crud/dict/cloneable", + component: "/crud/dict/cloneable/index.vue" + }, + { + title: "原型复制", + name: "DictPrototype", + path: "/crud/dict/prototype", + component: "/crud/dict/prototype/index.vue" + } + ] + }, + { + title: "操作列", + name: "row-handle", + path: "/crud/row-handle", + redirect: "/crud/row-handle/tooltip", + meta: { + icon: "ion:build-outline" + }, + children: [ + { + title: "Tooltip", + name: "RowHandleTooltip", + path: "/crud/row-handle/tooltip", + component: "/crud/row-handle/tooltip/index.vue" + }, + { + title: "按钮折叠", + name: "RowHandleDropdown", + path: "/crud/row-handle/dropdown", + component: "/crud/row-handle/dropdown/index.vue" + } + ] + }, + { + title: "组件示例", + name: "component", + path: "/crud/component", + redirect: "/crud/component/text", + meta: { + icon: "ion:cube-outline" + }, + children: [ + { + title: "文本输入(input)", + name: "ComponentText", + path: "/crud/component/text", + component: "/crud/component/text/index.vue" + }, + { + title: "选择(select)", + name: "ComponentSelect", + path: "/crud/component/select", + component: "/crud/component/select/index.vue" + }, + { + title: "级联(cascader)", + name: "ComponentCascader", + path: "/crud/component/cascader", + component: "/crud/component/cascader/index.vue" + }, + { + title: "多选(checkbox)", + name: "ComponentCheckbox", + path: "/crud/component/checkbox", + component: "/crud/component/checkbox/index.vue" + }, + { + title: "单选(radio)", + name: "ComponentRadio", + path: "/crud/component/radio", + component: "/crud/component/radio/index.vue" + }, + { + title: "开关(switch)", + name: "ComponentSwitch", + path: "/crud/component/switch", + component: "/crud/component/switch/index.vue" + }, + { + title: "日期时间(date)", + name: "ComponentDate", + path: "/crud/component/date", + component: "/crud/component/date/index.vue" + }, + { + title: "按钮链接", + name: "ComponentButton", + path: "/crud/component/button", + component: "/crud/component/button/index.vue" + }, + { + title: "数字", + name: "ComponentNumber", + path: "/crud/component/number", + component: "/crud/component/number/index.vue" + }, + { + title: "树形选择", + name: "ComponentTree", + path: "/crud/component/tree", + component: "/crud/component/tree/index.vue" + }, + { + title: "图片裁剪上传", + name: "ComponentUploaderCropper", + path: "/crud/component/uploader/cropper", + component: "/crud/component/uploader/cropper/index.vue" + }, + { + title: "表单本地上传", + name: "ComponentUploaderForm", + path: "/crud/component/uploader/form", + component: "/crud/component/uploader/form/index.vue" + }, + { + title: "阿里云oss上传", + name: "ComponentUploaderAlioss", + path: "/crud/component/uploader/alioss", + component: "/crud/component/uploader/alioss/index.vue" + }, + { + title: "腾讯云cos上传", + name: "ComponentUploaderCos", + path: "/crud/component/uploader/cos", + component: "/crud/component/uploader/cos/index.vue" + }, + { + title: "七牛云上传", + name: "ComponentUploaderQiniu", + path: "/crud/component/uploader/qiniu", + component: "/crud/component/uploader/qiniu/index.vue" + }, + { + title: "富文本编辑器", + name: "ComponentEditor", + path: "/crud/component/editor", + component: "/crud/component/editor/index.vue" + }, + { + title: "图标", + name: "ComponentIcon", + path: "/crud/component/icon", + component: "/crud/component/icon/index.vue" + }, + { + title: "JsonEditor", + name: "ComponentJson", + path: "/crud/component/json", + component: "/crud/component/json/index.vue" + } + ] + }, + { + title: "Form表单", + name: "form", + path: "/crud/form", + redirect: "/crud/form/layout", + meta: { + icon: "ion:document-text-outline" + }, + children: [ + { + title: "基本表单", + name: "FormBase", + path: "/crud/form/base", + component: "/crud/form/base/index.vue" + }, + { + title: "表单Grid布局", + name: "FormLayoutGrid", + path: "/crud/form/layout-grid", + component: "/crud/form/layout-grid/index.vue" + }, + { + title: "表单Flex布局", + name: "FormLayoutFlex", + path: "/crud/form/layout-flex", + component: "/crud/form/layout-flex/index.vue" + }, + { + title: "表单动态布局", + name: "FormLayout", + path: "/crud/form/layout", + component: "/crud/form/layout/index.vue" + }, + { + title: "表单校验", + name: "FormValidation", + path: "/crud/form/validation", + component: "/crud/form/validation/index.vue" + }, + { + title: "抽屉表单", + name: "FormDrawer", + path: "/crud/form/drawer", + component: "/crud/form/drawer/index.vue" + }, + { + title: "表单分组", + name: "FormGroup", + path: "/crud/form/group", + component: "/crud/form/group/index.vue" + }, + { + title: "表单分组(tabs)", + name: "FormGroupTabs", + path: "/crud/form/group-tabs", + component: "/crud/form/group-tabs/index.vue" + }, + { + title: "自定义表单", + name: "FormCustomForm", + path: "/crud/form/custom-form", + component: "/crud/form/custom-form/index.vue" + }, + { + title: "字段帮助说明", + name: "FormHelper", + path: "/crud/form/helper", + component: "/crud/form/helper/index.vue" + }, + { + title: "页面内部弹出表单", + name: "FormInner", + path: "/crud/form/inner", + component: "/crud/form/inner/index.vue", + meta: { + cache: true + } + }, + { + title: "地区字典管理", + name: "FormInnerArea", + path: "/crud/form/inner/area", + component: "/crud/form/inner/area/index.vue", + meta: { + isMenu: false + } + }, + { + title: "新页面编辑", + name: "FormNewPage", + path: "/crud/form/new-page", + component: "/crud/form/new-page/index.vue", + meta: { + cache: false + } + }, + { + title: "新页面编辑表单", + name: "FormNewPageEdit", + path: "/crud/form/new-page/edit", + component: "/crud/form/new-page/edit.vue", + meta: { + isMenu: false + } + }, + { + title: "独立使用表单", + name: "FormIndependent", + path: "/crud/form/independent", + component: "/crud/form/independent/index.vue" + }, + { + title: "重置表单", + name: "FormReset", + path: "/crud/form/reset", + component: "/crud/form/reset/index.vue" + }, + { + title: "嵌套数据结构", + name: "FormNest", + path: "/crud/form/nest", + component: "/crud/form/nest/index.vue" + } + ] + }, + { + title: "表格特性", + path: "/crud/feature", + meta: { + icon: "ion:beer-outline" + }, + redirect: "/crud/feature/dropdown", + children: [ + { + title: "部件显隐", + name: "FeatureHide", + path: "/crud/feature/hide", + component: "/crud/feature/hide/index.vue" + }, + { + title: "多选&批量删除", + name: "FeatureSelection", + path: "/crud/feature/selection", + component: "/crud/feature/selection/index.vue" + }, + { + title: "单选", + name: "FeatureSelectionRadio", + path: "/crud/feature/selection-radio", + component: "/crud/feature/selection-radio/index.vue" + }, + { + title: "表头过滤", + name: "FeatureFilter", + path: "/crud/feature/filter", + component: "/crud/feature/filter/index.vue" + }, + { + title: "行展开", + name: "FeatureExpand", + path: "/crud/feature/expand", + component: "/crud/feature/expand/index.vue" + }, + { + title: "树形表格", + name: "FeatureTree", + path: "/crud/feature/tree", + component: "/crud/feature/tree/index.vue" + }, + { + title: "多级表头", + name: "FeatureHeaderGroup", + path: "/crud/feature/header-group", + component: "/crud/feature/header-group/index.vue" + }, + { + title: "合并单元格", + name: "FeatureMerge", + path: "/crud/feature/merge", + component: "/crud/feature/merge/index.vue" + }, + { + title: "序号", + name: "FeatureIndex", + path: "/crud/feature/index", + component: "/crud/feature/index/index.vue" + }, + { + title: "排序", + name: "FeatureSortable", + path: "/crud/feature/sortable", + component: "/crud/feature/sortable/index.vue" + }, + { + title: "固定列", + name: "FeatureFixed", + path: "/crud/feature/fixed", + component: "/crud/feature/fixed/index.vue" + }, + { + title: "不固定高度", + name: "FeatureHeight", + path: "/crud/feature/height", + component: "/crud/feature/height/index.vue" + }, + { + title: "可编辑", + name: "FeatureEditable", + path: "/crud/feature/editable", + component: "/crud/feature/editable/index.vue" + }, + { + title: "行编辑", + name: "FeatureEditableRow", + path: "/crud/feature/editable-row", + component: "/crud/feature/editable-row/index.vue" + }, + { + title: "查询框", + name: "FeatureSearch", + path: "/crud/feature/search", + component: "/crud/feature/search/index.vue" + }, + { + title: "查询框多行模式", + name: "FeatureSearchMulti", + path: "/crud/feature/search-multi", + component: "/crud/feature/search-multi/index.vue" + }, + { + title: "字段排序", + name: "FeatureColumnSort", + path: "/crud/feature/column-sort", + component: "/crud/feature/column-sort/index.vue" + }, + { + title: "ValueBuilder", + name: "FeatureValueBuilder", + path: "/crud/feature/value-builder", + component: "/crud/feature/value-builder/index.vue" + }, + { + title: "列设置", + name: "FeatureColumnsSet", + path: "/crud/feature/columns-set", + component: "/crud/feature/columns-set/index.vue" + }, + { + title: "本地化编辑", + name: "FeatureLocal", + path: "/crud/feature/local", + component: "/crud/feature/local/index.vue" + }, + { + title: "v-model", + name: "FeatureVModel", + path: "/crud/feature/v-model", + component: "/crud/feature/local-v-model/index.vue" + }, + { + title: "自定义删除", + name: "FeatureRemove", + path: "/crud/feature/remove", + component: "/crud/feature/remove/index.vue" + }, + { + title: "调整列宽", + name: "FeatureColumnResize", + path: "/crud/feature/column-resize", + component: "/crud/feature/column-resize/index.vue" + } + ] + }, + { + title: "插槽", + name: "Slots", + path: "/crud/slots", + redirect: "/crud/slots/layout", + meta: { + icon: "ion:extension-puzzle-outline" + }, + children: [ + { + title: "页面占位插槽", + name: "SlotsLayout", + path: "/crud/slots/layout", + component: "/crud/slots/layout/index.vue" + }, + { + title: "表单占位插槽", + name: "SlotsForm", + path: "/crud/slots/form", + component: "/crud/slots/form/index.vue" + }, + { + title: "查询字段插槽", + name: "SlotsSearch", + path: "/crud/slots/search", + component: "/crud/slots/search/index.vue" + }, + { + title: "单元格插槽", + name: "SlotsCell", + path: "/crud/slots/cell", + component: "/crud/slots/cell/index.vue" + }, + { + title: "表单字段插槽", + name: "SlotsFormItem", + path: "/crud/slots/form-item", + component: "/crud/slots/form-item/index.vue" + } + ] + }, + { + title: "复杂需求", + name: "Advanced", + path: "/crud/advanced", + redirect: "/crud/advanced/linkage", + meta: { + icon: "ion:flame-outline" + }, + children: [ + { + title: "选择联动", + name: "AdvancedLinkage", + path: "/crud/advanced/linkage", + component: "/crud/advanced/linkage/index.vue" + }, + { + title: "后台加载crud", + name: "AdvancedFormBackend", + path: "/crud/advanced/from-backend", + component: "/crud/advanced/from-backend/index.vue" + }, + { + title: "本地分页", + name: "AdvancedLocalPagination", + path: "/crud/advanced/local-pagination", + component: "/crud/advanced/local-pagination/index.vue" + }, + { + title: "嵌套子表格", + name: "AdvancedNest", + path: "/crud/advanced/nest", + component: "/crud/advanced/nest/index.vue" + }, + { + title: "对话框中显示crud", + name: "AdvancedInDialog", + path: "/crud/advanced/in-dialog", + component: "/crud/advanced/in-dialog/index.vue" + }, + { + title: "大量数据", + name: "AdvancedBigData", + path: "/crud/advanced/big-data", + component: "/crud/advanced/big-data/index.vue" + } + ] + } + ] + } +]; diff --git a/packages/ui/certd-client/src/router/source/modules/sys.ts b/packages/ui/certd-client/src/router/source/modules/sys.ts new file mode 100644 index 000000000..a2d751271 --- /dev/null +++ b/packages/ui/certd-client/src/router/source/modules/sys.ts @@ -0,0 +1,61 @@ +import LayoutPass from "/@/layout/layout-pass.vue"; + +export const sysResources = [ + { + title: "系统管理", + name: "sys", + path: "/sys", + redirect: "/sys/authority", + component: LayoutPass, + meta: { + icon: "ion:settings-outline", + permission: "sys" + }, + children: [ + { + title: "权限管理", + name: "authority", + path: "/sys/authority", + redirect: "/sys/authority/permission", + meta: { + icon: "ion:ribbon-outline", + //需要校验权限 + permission: "sys:auth" + }, + children: [ + { + title: "权限资源管理", + name: "permission", + meta: { + icon: "ion:list-outline", + //需要校验权限 + permission: "sys:auth:per:view" + }, + path: "/sys/authority/permission", + component: "/sys/authority/permission/index.vue" + }, + { + title: "角色管理", + name: "role", + meta: { + icon: "ion:people-outline", + permission: "sys:auth:role:view" + }, + path: "/sys/authority/role", + component: "/sys/authority/role/index.vue" + } + ] + }, + { + title: "用户管理", + name: "user", + meta: { + icon: "ion:person-outline", + permission: "sys:auth:user:view" + }, + path: "/sys/authority/user", + component: "/sys/authority/user/index.vue" + } + ] + } +]; diff --git a/packages/ui/certd-client/src/router/source/outside.ts b/packages/ui/certd-client/src/router/source/outside.ts new file mode 100644 index 000000000..177172c35 --- /dev/null +++ b/packages/ui/certd-client/src/router/source/outside.ts @@ -0,0 +1,22 @@ +import LayoutOutside from "/src/layout/layout-outside.vue"; +import Error404 from "/src/views/framework/error/404.vue"; +const errorPage = [{ path: "/:pathMatch(.*)*", name: "not-found", component: Error404 }]; +export const outsideResource = [ + { + title: "outside", + name: "outside", + path: "/outside", + component: LayoutOutside, + children: [ + { + meta: { + title: "登录" + }, + name: "login", + path: "/login", + component: "/framework/login/index.vue" + } + ] + }, + ...errorPage +]; diff --git a/packages/ui/certd-client/src/shims-vue.d.ts b/packages/ui/certd-client/src/shims-vue.d.ts new file mode 100644 index 000000000..daba9b9ec --- /dev/null +++ b/packages/ui/certd-client/src/shims-vue.d.ts @@ -0,0 +1,5 @@ +declare module "*.vue" { + import { DefineComponent } from "vue"; + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/packages/ui/certd-client/src/store/index.ts b/packages/ui/certd-client/src/store/index.ts new file mode 100644 index 000000000..52b9cf688 --- /dev/null +++ b/packages/ui/certd-client/src/store/index.ts @@ -0,0 +1,9 @@ +import { createPinia } from "pinia"; +const store = createPinia(); +export default { + install(app) { + app.use(store); + } +}; + +export { store }; diff --git a/packages/ui/certd-client/src/store/modules/page.ts b/packages/ui/certd-client/src/store/modules/page.ts new file mode 100644 index 000000000..8c2d7176d --- /dev/null +++ b/packages/ui/certd-client/src/store/modules/page.ts @@ -0,0 +1,436 @@ +import { defineStore } from "pinia"; +import { cloneDeep, get, uniq } from "lodash-es"; +import router from "/src/router"; +import { frameworkRoutes } from "/src/router/resolve"; +// @ts-ignore +import { LocalStorage } from "/src/utils/util.storage"; +import { useUserStore } from "/src/store/modules/user"; +const OPENED_CACHE_KEY = "TABS_OPENED"; + +interface PageState { + // 可以在多页 tab 模式下显示的页面 + pool: Array; + // 当前显示的多页面列表 + opened: Array; + // 已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201 + openedLoaded: boolean; + // 当前页面 + current: ""; + // 需要缓存的页面 name + keepAlive: Array; + inited: boolean; +} +// 判定是否需要缓存 +const isKeepAlive = (data) => get(data, "meta.cache", false); + +export const usePageStore = defineStore({ + id: "app.page", + state: (): PageState => ({ + // 可以在多页 tab 模式下显示的页面 + pool: [], + // 当前显示的多页面列表 + opened: [ + { + name: "index", + fullPath: "/index", + meta: { + title: "首页", + auth: false + } + } + ], + // 已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201 + openedLoaded: false, + // 当前页面 + current: "", + // 需要缓存的页面 name + keepAlive: [], + inited: false + }), + getters: { + getOpened() { + // @ts-ignore + return this.opened; + }, + getCurrent(): string { + return this.current; + } + }, + actions: { + /** + * @description 确认已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201 + * @param {Object} context + */ + async isLoaded() { + if (this.openedLoaded) { + return true; + } + return new Promise((resolve) => { + const timer = setInterval(() => { + if (this.openedLoaded) { + resolve(clearInterval(timer)); + } + }, 10); + }); + }, + /** + * @class opened + * @description 从持久化数据载入标签页列表 + * @param {Object} context + */ + async openedLoad() { + // store 赋值 + const value = LocalStorage.get(this.getStorageKey()); + if (value == null) { + return; + } + // 在处理函数中进行数据优化 过滤掉现在已经失效的页签或者已经改变了信息的页签 + // 以 fullPath 字段为准 + // 如果页面过多的话可能需要优化算法 + // valid 有效列表 1, 1, 0, 1 => 有效, 有效, 失效, 有效 + const valid: Array = []; + // 处理数据 + this.opened = value + .map((opened) => { + // 忽略首页 + if (opened.fullPath === "/index") { + valid.push(1); + return opened; + } + // 尝试在所有的支持多标签页的页面里找到 name 匹配的页面 + const find = this.pool.find((item) => item.name === opened.name); + // 记录有效或无效信息 + valid.push(find ? 1 : 0); + // 返回合并后的数据 新的覆盖旧的 + // 新的数据中一般不会携带 params 和 query, 所以旧的参数会留存 + return Object.assign({}, opened, find); + }) + .filter((opened, index) => valid[index] === 1); + // 标记已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201 + this.openedLoaded = true; + // 根据 opened 数据生成缓存设置 + this.keepAliveRefresh(); + }, + + getStorageKey() { + const userStore = useUserStore(); + const userId = userStore.getUserInfo?.id ?? "anonymous"; + return OPENED_CACHE_KEY + ":" + userId; + }, + /** + * 将 opened 属性赋值并持久化 在这之前请先确保已经更新了 state.opened + * @param {Object} context + */ + async opened2db() { + // 设置数据 + + LocalStorage.set(this.getStorageKey(), this.opened); + }, + /** + * @class opened + * @description 更新页面列表上的某一项 + * @param {Object} context + * @param {Object} payload { index, params, query, fullPath } 路由信息 + */ + async openedUpdate({ index, params, query, fullPath }) { + // 更新页面列表某一项 + const page = this.opened[index]; + page.params = params || page.params; + page.query = query || page.query; + page.fullPath = fullPath || page.fullPath; + this.opened.splice(index, 1, page); + // 持久化 + await this.opened2db(); + }, + /** + * @class opened + * @description 重排页面列表上的某一项 + * @param {Object} context + * @param {Object} payload { oldIndex, newIndex } 位置信息 + */ + async openedSort({ oldIndex, newIndex }) { + // 重排页面列表某一项 + const page = this.opened[oldIndex]; + this.opened.splice(oldIndex, 1); + this.opened.splice(newIndex, 0, page); + // 持久化 + await this.opened2db(); + }, + /** + * @class opened + * @description 新增一个 tag (打开一个页面) + * @param {Object} context + * @param {Object} payload new tag info + */ + async add({ tag, params, query, fullPath }) { + // 设置新的 tag 在新打开一个以前没打开过的页面时使用 + const newTag = tag; + newTag.params = params || newTag.params; + newTag.query = query || newTag.query; + newTag.fullPath = fullPath || newTag.fullPath; + // 添加进当前显示的页面数组 + this.opened.push(newTag); + // 如果这个页面需要缓存 将其添加到缓存设置 + if (isKeepAlive(newTag)) { + this.keepAlivePush(tag.name); + } + // 持久化 + await this.opened2db(); + }, + /** + * @class current + * @description 打开一个新的页面 + * @param {Object} context + * @param {Object} payload 从路由钩子的 to 对象上获取 { name, params, query, fullPath, meta } 路由信息 + */ + async open({ name, params, query, fullPath, meta }) { + // 已经打开的页面 + const opened = this.opened; + // 判断此页面是否已经打开 并且记录位置 + let pageOpendIndex = 0; + const pageOpend = opened.find((page, index) => { + const same = page.fullPath === fullPath; + pageOpendIndex = same ? index : pageOpendIndex; + return same; + }); + if (pageOpend) { + // 页面以前打开过 + await this.openedUpdate({ + index: pageOpendIndex, + params, + query, + fullPath + }); + } else { + // 页面以前没有打开过 + const page = this.pool.find((t) => t.name === name); + // 如果这里没有找到 page 代表这个路由虽然在框架内 但是不参与标签页显示 + if (page) { + this.add({ + tag: Object.assign({}, page), + params, + query, + fullPath + }); + } + } + // 如果这个页面需要缓存 将其添加到缓存设置 + if (isKeepAlive({ meta })) { + this.keepAlivePush(name); + } + // 设置当前的页面 + this.currentSet(fullPath); + }, + /** + * @class opened + * @description 关闭一个 tag (关闭一个页面) + * @param {Object} context + * @param {Object} payload { tagName: 要关闭的标签名字 } + */ + async close({ tagName }) { + // 预定下个新页面 + let newPage = {}; + const isCurrent = this.current === tagName; + // 如果关闭的页面就是当前显示的页面 + if (isCurrent) { + // 去找一个新的页面 + const len = this.opened.length; + for (let i = 0; i < len; i++) { + if (this.opened[i].fullPath === tagName) { + newPage = i < len - 1 ? this.opened[i + 1] : this.opened[i - 1]; + break; + } + } + } + // 找到这个页面在已经打开的数据里是第几个 + const index = this.opened.findIndex((page) => page.fullPath === tagName); + if (index >= 0) { + // 如果这个页面是缓存的页面 将其在缓存设置中删除 + this.keepAliveRemove(this.opened[index].name); + // 更新数据 删除关闭的页面 + this.opened.splice(index, 1); + } + // 持久化 + await this.opened2db(); + // 决定最后停留的页面 + if (isCurrent) { + // @ts-ignore + const { name = "index", params = {}, query = {} } = newPage; + const routerObj = { name, params, query }; + await router.push(routerObj); + } + }, + /** + * @class opened + * @description 关闭当前标签左边的标签 + * @param opts + */ + async closeLeft(opts = {}) { + await this.closeByCondition({ + condition({ i, currentIndex }) { + return i >= currentIndex; + }, + ...opts + }); + }, + /** + * @class opened + * @description 关闭当前标签右边的标签 + * @param opts + */ + async closeRight(opts = {}) { + await this.closeByCondition({ + condition({ i, currentIndex }) { + return currentIndex >= i; + }, + ...opts + }); + }, + /** + * @class opened + * @description 关闭当前标签右边的标签 + * @param opts + */ + async closeByCondition(opts = {}) { + // @ts-ignore + const { pageSelect, condition } = opts; + const pageAim = pageSelect || this.current; + let currentIndex = 0; + this.opened.forEach((page, index) => { + if (page.fullPath === pageAim) currentIndex = index; + }); + // 删除打开的页面 并在缓存设置中删除 + for (let i = this.opened.length - 1; i >= 0; i--) { + if (this.opened[i].name === "index" || condition({ i, currentIndex })) { + continue; + } + this.keepAliveRemove(this.opened[i].name); + this.opened.splice(i, 1); + } + // 持久化 + await this.opened2db(); + // 设置当前的页面 + this.current = pageAim; + // @ts-ignore + if (router.currentRoute.fullPath !== pageAim) await router.push(pageAim); + }, + /** + * @class opened + * @description 关闭当前激活之外的 tag + * @param opts + */ + async closeOther(opts = {}) { + await this.closeByCondition({ + condition({ i, currentIndex }) { + return currentIndex === i; + }, + ...opts + }); + }, + /** + * @class opened + * @description 关闭所有 tag + * @param {Object} context + */ + async closeAll() { + // 删除打开的页面 并在缓存设置中删除 + for (let i = this.opened.length - 1; i >= 0; i--) { + if (this.opened[i].name === "index") { + continue; + } + + this.keepAliveRemove(this.opened[i].name); + this.opened.splice(i, 1); + } + // 持久化 + await this.opened2db(); + // 关闭所有的标签页后需要判断一次现在是不是在首页 + // @ts-ignore + if (router.currentRoute.name !== "index") { + await router.push({ name: "index" }); + } + }, + /** + * @class keepAlive + * @description 从已经打开的页面记录中更新需要缓存的页面记录 + * @param {Object} state state + */ + keepAliveRefresh() { + this.keepAlive = this.opened.filter((item) => isKeepAlive(item)).map((e) => e.name); + console.log("keep alive:", this.keepAlive); + }, + /** + * @description 删除一个页面的缓存设置 + * @param {Object} state state + * @param {String} name name + */ + keepAliveRemove(name) { + const list = cloneDeep(this.keepAlive); + const index = list.findIndex((item) => item === name); + if (index !== -1) { + list.splice(index, 1); + this.keepAlive = list; + } + }, + /** + * @description 增加一个页面的缓存设置 + * @param {Object} state state + * @param {String} name name + */ + keepAlivePush(name) { + const keep = cloneDeep(this.keepAlive); + keep.push(name); + this.keepAlive = uniq(keep); + }, + /** + * @description 清空页面缓存设置 + * @param {Object} state state + */ + keepAliveClean() { + this.keepAlive = []; + }, + /** + * @class current + * @description 设置当前激活的页面 fullPath + * @param {Object} state state + * @param {String} fullPath new fullPath + */ + currentSet(fullPath) { + this.current = fullPath; + }, + /** + * @class pool + * @description 保存 pool (候选池) + * @param {Object} state state + * @param {Array} routes routes + */ + async init(routes) { + if (this.inited) { + return; + } + this.inited = true; + if (routes == null) { + //不能用全部的routes,只能是framework内的 + routes = frameworkRoutes; + } + + const pool = []; + const push = function (routes) { + routes.forEach((route) => { + if (route.children && route.children.length > 0) { + push(route.children); + } else { + if (!route.hidden) { + const { meta, name, path } = route; + // @ts-ignore + pool.push({ meta, name, path }); + } + } + }); + }; + push(routes); + this.pool = pool; + await this.openedLoad(); + } + } +}); diff --git a/packages/ui/certd-client/src/store/modules/resource.ts b/packages/ui/certd-client/src/store/modules/resource.ts new file mode 100644 index 000000000..33674c972 --- /dev/null +++ b/packages/ui/certd-client/src/store/modules/resource.ts @@ -0,0 +1,127 @@ +import { defineStore } from "pinia"; +// @ts-ignore +import { frameworkMenus, headerMenus, filterMenus, findMenus } from "/src/router/resolve"; +import _ from "lodash-es"; +import { mitter } from "/src/utils/util.mitt"; +//监听注销事件 +mitter.on("app.logout", () => { + const resourceStore = useResourceStore(); + resourceStore.clear(); +}); + +interface ResourceState { + frameworkMenus: Array; + headerMenus: Array; + asideMenus: Array; + fixedAsideMenus: Array; + inited: boolean; + currentAsidePath: string; +} + +export const useResourceStore = defineStore({ + id: "app.resource", + state: (): ResourceState => ({ + // user info + frameworkMenus: [], + headerMenus: [], + asideMenus: [], + fixedAsideMenus: [], + inited: false, + currentAsidePath: "" + }), + getters: { + getAsideMenus() { + return this.asideMenus; + }, + getHeaderMenus() { + return this.headerMenus; + }, + getFrameworkMenus() { + return this.frameworkMenus; + } + }, + actions: { + clear() { + this.inited = false; + }, + /** + * 初始化资源 + */ + init() { + if (this.inited) { + return; + } + this.inited = true; + + const showMenus = _.cloneDeep(frameworkMenus[0].children); + this.frameworkMenus = filterMenus(showMenus, (item) => { + return item?.meta?.showOnHeader !== false; + }); + + this.fixedAsideMenus = findMenus(showMenus, (item) => { + return item?.meta?.fixedAside === true; + }); + this.headerMenus = headerMenus; + this.setAsideMenu(); + }, + setAsideMenu(topMenu?) { + if (this.frameworkMenus.length === 0) { + return; + } + if (topMenu == null) { + topMenu = this.frameworkMenus[0]; + } + const asideMenus = topMenu?.children || []; + this.asideMenus = [...this.fixedAsideMenus, ...asideMenus]; + }, + setAsideMenuByCurrentRoute(matched) { + const menuHeader = this.frameworkMenus; + if (matched?.length <= 1) { + return; + } + + function findFromTree(tree, find) { + const results: Array = []; + for (const item of tree) { + if (find(item)) { + results.push(item); + return results; + } + if (item.children && item.children.length > 0) { + const found = findFromTree(item.children, find); + if (found) { + results.push(item); + return results.concat(found); + } + } + } + } + const matchedPath = matched[1].path; + const _side = findFromTree(menuHeader, (menu) => menu.path === matchedPath); + if (_side?.length > 0) { + if (this.currentAsidePath === _side[0]) { + return; + } + this.currentAsidePath = _side[0]; + this.setAsideMenu(_side[0]); + } + }, + filterByPermission(permissions) { + this.frameworkMenus = this.filterChildrenByPermission(this.frameworkMenus, permissions); + }, + filterChildrenByPermission(list, permissions) { + const menus = list.filter((item) => { + if (item?.meta?.permission) { + return permissions.includes(item.meta.permission); + } + return true; + }); + for (const menu of menus) { + if (menu.children && menu.children.length > 0) { + menu.children = this.filterChildrenByPermission(menu.children, permissions); + } + } + return menus; + } + } +}); diff --git a/packages/ui/certd-client/src/store/modules/settings.ts b/packages/ui/certd-client/src/store/modules/settings.ts new file mode 100644 index 000000000..a6a73dd58 --- /dev/null +++ b/packages/ui/certd-client/src/store/modules/settings.ts @@ -0,0 +1,63 @@ +import { defineStore } from "pinia"; +// @ts-ignore +import { LocalStorage } from "/src/utils/util.storage"; +// import { replaceStyleVariables } from "vite-plugin-theme/es/client"; + +// import { getThemeColors, generateColors } from "/src/../build/theme-colors"; +// +// import { mixLighten, mixDarken, tinycolor } from "vite-plugin-theme/es/colorUtils"; + +// export async function changeTheme(color?: string) { +// if (color == null) { +// return; +// } +// const colors = generateColors({ +// mixDarken, +// mixLighten, +// tinycolor, +// color +// }); +// +// return await replaceStyleVariables({ +// colorVariables: [...getThemeColors(color), ...colors] +// }); +// } + +interface SettingState { + theme: any; +} + +const SETTING_THEME_KEY = "SETTING_THEME"; +export const useSettingStore = defineStore({ + id: "app.setting", + state: (): SettingState => ({ + // user info + theme: null + }), + getters: { + getTheme(): any { + return this.theme || LocalStorage.get(SETTING_THEME_KEY) || {}; + } + }, + actions: { + persistTheme() { + LocalStorage.set(SETTING_THEME_KEY, this.getTheme); + }, + async setTheme(theme?: Object) { + if (theme == null) { + theme = this.getTheme; + } + this.theme = theme; + this.persistTheme(); + // await changeTheme(this.theme.primaryColor); + }, + async setPrimaryColor(color) { + const theme = this.theme; + theme.primaryColor = color; + await this.setTheme(); + }, + async init() { + await this.setTheme(this.getTheme); + } + } +}); diff --git a/packages/ui/certd-client/src/store/modules/user.ts b/packages/ui/certd-client/src/store/modules/user.ts new file mode 100644 index 000000000..45ed7b29b --- /dev/null +++ b/packages/ui/certd-client/src/store/modules/user.ts @@ -0,0 +1,106 @@ +import { defineStore } from "pinia"; +import { store } from "../index"; +import router from "../../router"; +// @ts-ignore +import { LocalStorage } from "/src/utils/util.storage"; +// @ts-ignore +import * as UserApi from "/src/api/modules/api.user"; +// @ts-ignore +import { LoginReq, UserInfoRes } from "/@/api/modules/api.user"; +import { Modal } from "ant-design-vue"; +import { useI18n } from "vue-i18n"; + +import { mitter } from "/src/utils/util.mitt"; + +interface UserState { + userInfo: Nullable; + token?: string; +} + +const USER_INFO_KEY = "USER_INFO"; +const TOKEN_KEY = "TOKEN"; +export const useUserStore = defineStore({ + id: "app.user", + state: (): UserState => ({ + // user info + userInfo: null, + // token + token: undefined + }), + getters: { + getUserInfo(): UserInfoRes { + return this.userInfo || LocalStorage.get(USER_INFO_KEY) || {}; + }, + getToken(): string { + return this.token || LocalStorage.get(TOKEN_KEY); + } + }, + actions: { + setToken(info: string, expire: number) { + this.token = info; + LocalStorage.set(TOKEN_KEY, this.token, expire); + }, + setUserInfo(info: UserInfoRes) { + this.userInfo = info; + LocalStorage.set(USER_INFO_KEY, info); + }, + resetState() { + this.userInfo = null; + this.token = ""; + LocalStorage.remove(TOKEN_KEY); + LocalStorage.remove(USER_INFO_KEY); + }, + /** + * @description: login + */ + async login(params: LoginReq): Promise { + try { + const data = await UserApi.login(params); + const { token, expire } = data; + + // save token + this.setToken(token, expire); + // get user info + const userInfo = await this.getUserInfoAction(); + await router.replace("/"); + mitter.emit("app.login", { userInfo, token: data }); + return userInfo; + } catch (error) { + return null; + } + }, + async getUserInfoAction(): Promise { + const userInfo = await UserApi.mine(); + this.setUserInfo(userInfo); + return userInfo; + }, + /** + * @description: logout + */ + logout(goLogin = true) { + this.resetState(); + goLogin && router.push("/login"); + mitter.emit("app.logout"); + }, + + /** + * @description: Confirm before logging out + */ + confirmLoginOut() { + const { t } = useI18n(); + Modal.config({ + iconType: "warning", + title: t("app.login.logoutTip"), + content: t("app.login.logoutMessage"), + onOk: async () => { + await this.logout(true); + } + }); + } + } +}); + +// Need to be used outside the setup +export function useUserStoreWidthOut() { + return useUserStore(store); +} diff --git a/packages/ui/certd-client/src/style.css b/packages/ui/certd-client/src/style.css new file mode 100644 index 000000000..211ee80a8 --- /dev/null +++ b/packages/ui/certd-client/src/style.css @@ -0,0 +1 @@ +/*占位,勿删*/ diff --git a/packages/ui/certd-client/src/style/common.less b/packages/ui/certd-client/src/style/common.less new file mode 100644 index 000000000..5edfd8dbf --- /dev/null +++ b/packages/ui/certd-client/src/style/common.less @@ -0,0 +1,124 @@ +@import './theme/index.less'; +@import './theme/default.less'; +@import './scroll.less'; +@import './transition.less'; +@import './fix-windicss.less'; +svg { vertical-align: baseline; } +html, body { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + box-sizing: border-box; +} + +div#app { + height: 100% +} + +h1, h2, h3, h4, h5, h6 { + margin-bottom: 0; +} + +.ant-btn-link { + height: 24px; +} + + +.ant-input-affix-wrapper { + padding: 4px 11px; +} + + +.anticon { + vertical-align: 0 !important; +} + + +.text-center{ + text-align: center; +} + +.red{ + color:red +} + +.font12{ + font-size: 12px; +} + +.bg-gray{ + background-color: #eee; +} + +.bg-white{ + background-color: #fff; +} + +.fs-page-header{ + background-color: #fff; +} + + +.ant-btn{ + .fs-iconify{ + font-size:16px; + margin-right:3px + } + + display: inline-flex; + justify-content: center; + align-items: center; + +} + +.ant-timeline{ + .fs-iconify{ + font-size: 16px; + } +} + +.mt-10{ + margin-top:10px; +} +.ml-5{ + margin-left:5px; +} +.ml-10{ + margin-left: 10px; +} +.mtb-5{ + margin: 5px 0 5px 0; +} + +.mb-10{ + margin-bottom: 10px; +} + +.mlr-5{ + margin: 0 5px 0 5px; +} + +.gray{ + color:gray; +} +.green{ + color:green; +} +.blue{ + color:blue; +} +.red{ + color:red +} +.yellow{ + color:yellow; +} + +.ml-2{ + margin-left: 2px; +} + +.font-20{ + font-size:20px +} \ No newline at end of file diff --git a/packages/ui/certd-client/src/style/fix-windicss.less b/packages/ui/certd-client/src/style/fix-windicss.less new file mode 100644 index 000000000..c4e284ecc --- /dev/null +++ b/packages/ui/certd-client/src/style/fix-windicss.less @@ -0,0 +1,3 @@ +img.ant-image-preview-img{ + display: initial; +} diff --git a/packages/ui/certd-client/src/style/scroll.less b/packages/ui/certd-client/src/style/scroll.less new file mode 100644 index 000000000..67b7a33ab --- /dev/null +++ b/packages/ui/certd-client/src/style/scroll.less @@ -0,0 +1,28 @@ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + width: 8px; + background: rgba(#101F1C, 0.1); + -webkit-border-radius: 2em; + -moz-border-radius: 2em; + border-radius: 2em; +} + +::-webkit-scrollbar-thumb { + // background-color: rgba(#101F1C, 0.5); + background-clip: padding-box; + min-height: 28px; + -webkit-border-radius: 2em; + -moz-border-radius: 2em; + border-radius: 2em; + background-color: #b3b3b3; + box-shadow: 0px 1px 1px #eee inset; +} + +::-webkit-scrollbar-thumb:hover { + + //background-color: rgba(#101F1C, 1); +} diff --git a/packages/ui/certd-client/src/style/theme/default.less b/packages/ui/certd-client/src/style/theme/default.less new file mode 100644 index 000000000..9b91b3456 --- /dev/null +++ b/packages/ui/certd-client/src/style/theme/default.less @@ -0,0 +1,27 @@ +.ant-layout{ + background-color: @bg-color; +} +.ant-layout-header { + background-color: @bg-color +} +.ant-layout-sider { + background-color:@bg-color +} +.ant-menu{ + background-color: @bg-color; + &.ant-menu-submenu-popup{ + background-color: transparent; + } +} +.aside-menu{ + .ant-menu-submenu > .ant-menu{ + background-color:@bg-color + } + + .ant-menu-item-active{ + background-color: @bg-menu-item-color; + } + .ant-menu-item-selected{ + background-color: @bg-menu-item-color !important; + } +} diff --git a/packages/ui/certd-client/src/style/theme/index.less b/packages/ui/certd-client/src/style/theme/index.less new file mode 100644 index 000000000..d83434fcf --- /dev/null +++ b/packages/ui/certd-client/src/style/theme/index.less @@ -0,0 +1,4 @@ +@primary-color: #1890ff; +// theme +@bg-color: #ebf1f6; +@bg-menu-item-color:hsla(0,0%,100%,.5); diff --git a/packages/ui/certd-client/src/style/transition.less b/packages/ui/certd-client/src/style/transition.less new file mode 100644 index 000000000..8ab0a2698 --- /dev/null +++ b/packages/ui/certd-client/src/style/transition.less @@ -0,0 +1,36 @@ +//.v-enter-from, +//.v-leave-to { +// opacity: 0; +//} +// +//.v-leave-from, +//.v-enter-to { +// opacity: 1; +//} +// 过渡动画 横向渐变 +.fade-transverse-leave-active, +.fade-transverse-enter-active { + transition: all .5s; +} +.fade-transverse-enter-from { + opacity: 0; + transform: translateX(-30px); +} +.fade-transverse-leave-to { + opacity: 0; + transform: translateX(30px); +} + +// 过渡动画 缩放渐变 +.fade-scale-leave-active, +.fade-scale-enter-active { + transition: all .3s; +} +.fade-scale-enter { + opacity: 0; + transform: scale(1.2); +} +.fade-scale-leave-to { + opacity: 0; + transform: scale(0.8); +} diff --git a/packages/ui/certd-client/src/types/global.d.ts b/packages/ui/certd-client/src/types/global.d.ts new file mode 100644 index 000000000..d40bbd681 --- /dev/null +++ b/packages/ui/certd-client/src/types/global.d.ts @@ -0,0 +1,99 @@ +import type { + ComponentRenderProxy, + VNode, + ComponentPublicInstance, + FunctionalComponent, + PropType as VuePropType, +} from 'vue'; + +declare global { + const __APP_INFO__: { + pkg: { + name: string; + version: string; + dependencies: Recordable; + devDependencies: Recordable; + }; + lastBuildTime: string; + }; + declare interface Window { + // Global vue app instance + __APP__: App; + } + + // vue + declare type PropType = VuePropType; + + export type Writable = { + -readonly [P in keyof T]: T[P]; + }; + + declare type Nullable = T | null; + declare type NonNullable = T extends null | undefined ? never : T; + declare type Recordable = Record; + declare type ReadonlyRecordable = { + readonly [key: string]: T; + }; + declare type Indexable = { + [key: string]: T; + }; + declare type DeepPartial = { + [P in keyof T]?: DeepPartial; + }; + declare type TimeoutHandle = ReturnType; + declare type IntervalHandle = ReturnType; + + declare interface ChangeEvent extends Event { + target: HTMLInputElement; + } + + declare interface WheelEvent { + path?: EventTarget[]; + } + interface ImportMetaEnv extends ViteEnv { + __: unknown; + } + + declare interface ViteEnv { + VITE_PORT: number; + VITE_USE_MOCK: boolean; + VITE_USE_PWA: boolean; + VITE_PUBLIC_PATH: string; + VITE_PROXY: [string, string][]; + VITE_GLOB_APP_TITLE: string; + VITE_GLOB_APP_SHORT_NAME: string; + VITE_USE_CDN: boolean; + VITE_DROP_CONSOLE: boolean; + VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none'; + VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean; + VITE_LEGACY: boolean; + VITE_USE_IMAGEMIN: boolean; + VITE_GENERATE_UI: string; + } + + declare function parseInt(s: string | number, radix?: number): number; + + declare function parseFloat(string: string | number): number; + + namespace JSX { + // tslint:disable no-empty-interface + type Element = VNode; + // tslint:disable no-empty-interface + type ElementClass = ComponentRenderProxy; + interface ElementAttributesProperty { + $props: any; + } + interface IntrinsicElements { + [elem: string]: any; + } + interface IntrinsicAttributes { + [elem: string]: any; + } + } +} + +declare module 'vue' { + export type JSXComponent = + | { new (): ComponentPublicInstance } + | FunctionalComponent; +} diff --git a/packages/ui/certd-client/src/utils/index.ts b/packages/ui/certd-client/src/utils/index.ts new file mode 100644 index 000000000..7e3f9afef --- /dev/null +++ b/packages/ui/certd-client/src/utils/index.ts @@ -0,0 +1,12 @@ +import * as envs from "./util.env"; +import * as sites from "./util.site"; +import * as storages from "./util.storage"; +import * as commons from "./util.common"; +import * as mitt from "./util.mitt"; +export const util = { + ...envs, + ...sites, + ...storages, + ...commons, + ...mitt +}; diff --git a/packages/ui/certd-client/src/utils/util.common.ts b/packages/ui/certd-client/src/utils/util.common.ts new file mode 100644 index 000000000..ca20b8c30 --- /dev/null +++ b/packages/ui/certd-client/src/utils/util.common.ts @@ -0,0 +1,33 @@ +import _ from "lodash-es"; +export default { + arrayToMap(array) { + if (!array) { + return {}; + } + if (!_.isArray(array)) { + return array; + } + const map = {}; + for (const item of array) { + if (item.key) { + map[item.key] = item; + } + } + return map; + }, + mapToArray(map) { + if (!map) { + return []; + } + if (_.isArray(map)) { + return map; + } + const array: any = []; + for (const key in map) { + const item = map[key]; + item.key = key; + array.push(item); + } + return array; + } +}; diff --git a/packages/ui/certd-client/src/utils/util.env.ts b/packages/ui/certd-client/src/utils/util.env.ts new file mode 100644 index 000000000..d87db9091 --- /dev/null +++ b/packages/ui/certd-client/src/utils/util.env.ts @@ -0,0 +1,40 @@ +import _ from "lodash-es"; +export function getEnvValue(key) { + // @ts-ignore + return import.meta.env["VITE_APP_" + key]; +} + +export class EnvConfig { + API; + MODE; + STORAGE; + TITLE; + PM_ENABLED; + constructor() { + this.init(); + } + + init() { + // @ts-ignore + _.forEach(import.meta.env, (value, key) => { + if (key.startsWith("VITE_APP")) { + key = key.replace("VITE_APP_", ""); + this[key] = value; + } + }); + // @ts-ignore + this.MODE = import.meta.env.MODE; + } + + get(key, defaultValue) { + return this[key] ?? defaultValue; + } + isDev() { + return this.MODE === "development" || this.MODE === "debug"; + } + isProd() { + return this.MODE === "production"; + } +} + +export const env = new EnvConfig(); diff --git a/packages/ui/certd-client/src/utils/util.mitt.ts b/packages/ui/certd-client/src/utils/util.mitt.ts new file mode 100644 index 000000000..be0f064f4 --- /dev/null +++ b/packages/ui/certd-client/src/utils/util.mitt.ts @@ -0,0 +1,2 @@ +import mitt from "mitt"; +export const mitter = mitt(); diff --git a/packages/ui/certd-client/src/utils/util.site.ts b/packages/ui/certd-client/src/utils/util.site.ts new file mode 100644 index 000000000..6305d396c --- /dev/null +++ b/packages/ui/certd-client/src/utils/util.site.ts @@ -0,0 +1,11 @@ +import { env } from "./util.env"; +export const site = { + /** + * @description 更新标题 + * @param {String} title 标题 + */ + title: function (titleText) { + const processTitle = env.TITLE || "FsAdmin"; + window.document.title = `${processTitle}${titleText ? ` | ${titleText}` : ""}`; + } +}; diff --git a/packages/ui/certd-client/src/utils/util.storage.ts b/packages/ui/certd-client/src/utils/util.storage.ts new file mode 100644 index 000000000..b9342da67 --- /dev/null +++ b/packages/ui/certd-client/src/utils/util.storage.ts @@ -0,0 +1,113 @@ +import { env } from "./util.env"; +function isNullOrUnDef(value) { + return value == null; +} +const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7; +export interface CreateStorageParams { + prefixKey: string; + storage: Storage; + timeout?: number; +} + +/** + *Cache class + *Construction parameters can be passed into sessionStorage, localStorage, + * @class Cache + * @example + */ +export class WebStorage { + private storage: Storage; + private prefixKey?: string; + private timeout?: number; + /** + * + * @param option + */ + constructor(option: Partial) { + this.storage = option.storage ?? localStorage; + this.prefixKey = option.prefixKey ?? getStorageShortName(); + this.timeout = option.timeout ?? DEFAULT_CACHE_TIME; + } + + private getKey(key: string) { + return `${this.prefixKey}${key}`.toUpperCase(); + } + + /** + * + * Set cache + * @param {string} key + * @param {*} value + * @param expire + * @expire Expiration time in seconds + * @memberof Cache + */ + set(key: string, value: any, expire: number | undefined = this.timeout) { + const stringData = JSON.stringify({ + value, + time: Date.now(), + expire: expire != null ? new Date().getTime() + expire * 1000 : null + }); + this.storage.setItem(this.getKey(key), stringData); + } + + /** + *Read cache + * @param {string} key + * @param def + * @memberof Cache + */ + get(key: string, def: any = null): any { + const val = this.storage.getItem(this.getKey(key)); + if (!val) return def; + + try { + const data = JSON.parse(val); + const { value, expire } = data; + if (isNullOrUnDef(expire) || expire >= new Date().getTime()) { + return value; + } + this.remove(key); + } catch (e) { + return def; + } + } + + /** + * Delete cache based on key + * @param {string} key + * @memberof Cache + */ + remove(key: string) { + this.storage.removeItem(this.getKey(key)); + } + + /** + * Delete all caches of this instance + */ + clear(): void { + this.storage.clear(); + } +} +export const createStorage = (option: Partial = {}): WebStorage => { + return new WebStorage(option); +}; + +export type Options = Partial; + +function getStorageShortName() { + return env.MODE + "_" + env.get("STORAGE", "certd") + "_"; +} + +export const createSessionStorage = (options: Options = {}): WebStorage => { + return createStorage({ storage: sessionStorage, ...options }); +}; + +export const createLocalStorage = (options: Options = {}): WebStorage => { + return createStorage({ storage: localStorage, timeout: DEFAULT_CACHE_TIME, ...options }); +}; + +export const SessionStorage = createSessionStorage(); +export const LocalStorage = createLocalStorage(); + +export default WebStorage; diff --git a/packages/ui/certd-client/src/views/certd/access/access-selector/access/crud.jsx b/packages/ui/certd-client/src/views/certd/access/access-selector/access/crud.jsx new file mode 100644 index 000000000..c780d6446 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/access/access-selector/access/crud.jsx @@ -0,0 +1,112 @@ +import * as api from "/@/views/certd/access/api"; +import { ref } from "vue"; +import { getCommonColumnDefine } from "/@/views/certd/access/common"; + +export default function ({ expose, props, ctx }) { + const { crudBinding } = expose; + const lastResRef = ref(); + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + form.type = props.type; + const res = await api.UpdateObj(form); + lastResRef.value = res; + return res; + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + form.type = props.type; + const res = await api.AddObj(form); + lastResRef.value = res; + return res; + }; + + const selectedRowKey = ref([props.modelValue]); + // watch( + // () => { + // return props.modelValue; + // }, + // (value) => { + // selectedRowKey.value = [value]; + // }, + // { + // immediate: true + // } + // ); + const onSelectChange = (changed) => { + selectedRowKey.value = changed; + ctx.emit("update:modelValue", changed[0]); + }; + + const typeRef = ref("aliyun"); + const commonColumnsDefine = getCommonColumnDefine(crudBinding, typeRef); + commonColumnsDefine.type.form.component.disabled = true; + return { + typeRef, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + toolbar: { + show: false + }, + search: { + show: false + }, + form: { + wrapper: { + width: "1050px" + } + }, + rowHandle: { + width: "150px" + }, + table: { + rowSelection: { + type: "radio", + selectedRowKeys: selectedRowKey, + onChange: onSelectChange + }, + customRow: (record) => { + return { + onClick: () => { + onSelectChange([record.id]); + } // 点击行 + }; + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + name: { + title: "名称", + search: { + show: true + }, + type: ["text"], + form: { + rules: [{ required: true, message: "请填写名称" }] + } + }, + ...commonColumnsDefine + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/certd/access/access-selector/access/index.vue b/packages/ui/certd-client/src/views/certd/access/access-selector/access/index.vue new file mode 100644 index 000000000..d4b603413 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/access/access-selector/access/index.vue @@ -0,0 +1,64 @@ + + + + diff --git a/packages/ui/certd-client/src/views/certd/access/access-selector/index.vue b/packages/ui/certd-client/src/views/certd/access/access-selector/index.vue new file mode 100644 index 000000000..b5eec9381 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/access/access-selector/index.vue @@ -0,0 +1,103 @@ + + + + diff --git a/packages/ui/certd-client/src/views/certd/access/api.js b/packages/ui/certd-client/src/views/certd/access/api.js new file mode 100644 index 000000000..50ad40fbd --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/access/api.js @@ -0,0 +1,49 @@ +import { request } from "/src/api/service"; +const apiPrefix = "/pi/access"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "post", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "post", + params: { id } + }); +} + +export function GetProviderDefine(type) { + return request({ + url: apiPrefix + "/define", + method: "post", + params: { type } + }); +} diff --git a/packages/ui/certd-client/src/views/certd/access/common.tsx b/packages/ui/certd-client/src/views/certd/access/common.tsx new file mode 100644 index 000000000..d9a829a59 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/access/common.tsx @@ -0,0 +1,85 @@ +import { dict } from "@fast-crud/fast-crud"; +import * as api from "./api"; +import _ from "lodash-es"; + +export function getCommonColumnDefine(crudBinding, typeRef) { + const AccessTypeDictRef = dict({ + url: "/pi/access/accessTypeDict" + }); + const defaultPluginConfig = { + component: { + name: "a-input", + vModel: "value" + } + }; + + function buildDefineFields(define, mode) { + const columns = crudBinding.value[mode + "Form"].columns; + for (const key in columns) { + if (key.indexOf(".") >= 0) { + delete columns[key]; + } + } + console.log('crudBinding.value[mode + "Form"].columns', columns); + _.forEach(define.input, (value, mapKey) => { + const key = "access." + mapKey; + const field = { + ...value, + key + }; + columns[key] = _.merge({ title: key }, defaultPluginConfig, field); + console.log("form", crudBinding.value[mode + "Form"]); + }); + } + + return { + type: { + title: "类型", + type: "dict-select", + dict: AccessTypeDictRef, + search: { + show: false + }, + form: { + component: { + disabled: false + }, + rules: [{ required: true, message: "请选择类型" }], + valueChange: { + immediate: true, + async handle({ value, mode, form }) { + if (value == null) { + return; + } + const define = await api.GetProviderDefine(value); + console.log("define", define); + buildDefineFields(define, mode); + } + } + }, + addForm: { + value: typeRef + } + }, + setting: { + column: { show: false }, + form: { + show: false, + valueBuilder({ value, form }) { + form.access = {}; + if (!value) { + return; + } + const setting = JSON.parse(value); + for (const key in setting) { + form.access[key] = setting[key]; + } + }, + valueResolve({ form }) { + const setting = form.access; + form.setting = JSON.stringify(setting); + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/certd/access/crud.tsx b/packages/ui/certd-client/src/views/certd/access/crud.tsx new file mode 100644 index 000000000..ccd5d23df --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/access/crud.tsx @@ -0,0 +1,61 @@ +import * as api from "./api"; +import { useI18n } from "vue-i18n"; +import { ref } from "vue"; +import { getCommonColumnDefine } from "/@/views/certd/access/common"; + +export default function ({ expose }) { + const { t } = useI18n(); + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const typeRef = ref(); + const { crudBinding } = expose; + const commonColumnsDefine = getCommonColumnDefine(crudBinding, typeRef); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + labelCol: { + span: 6 + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + name: { + title: "名称", + type: "text", + form: { + rules: [{ required: true, message: "必填项" }] + } + }, + ...commonColumnsDefine + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/certd/access/index.vue b/packages/ui/certd-client/src/views/certd/access/index.vue new file mode 100644 index 000000000..379456ba3 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/access/index.vue @@ -0,0 +1,44 @@ + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/api.history.ts b/packages/ui/certd-client/src/views/certd/pipeline/api.history.ts new file mode 100644 index 000000000..9cfc54d63 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/api.history.ts @@ -0,0 +1,35 @@ +import { request } from "/src/api/service"; +import { RunHistory } from "/@/views/certd/pipeline/pipeline/type"; + +const apiPrefix = "/pi/history"; + +export async function GetList(query) { + const list = await request({ + url: apiPrefix + "/list", + method: "post", + data: query + }); + for (const item of list) { + if (item.pipeline) { + item.pipeline = JSON.parse(item.pipeline); + } + } + console.log("history", list); + return list; +} + +export async function GetDetail(query): Promise { + const detail = await request({ + url: apiPrefix + "/detail", + method: "post", + params: query + }); + + const pipeline = JSON.parse(detail.history?.pipeline || "{}"); + const logs = JSON.parse(detail.log?.logs || "{}"); + return { + id: detail.history.id, + pipeline, + logs + } as RunHistory; +} diff --git a/packages/ui/certd-client/src/views/certd/pipeline/api.plugin.ts b/packages/ui/certd-client/src/views/certd/pipeline/api.plugin.ts new file mode 100644 index 000000000..42ad9842d --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/api.plugin.ts @@ -0,0 +1,38 @@ +import { request } from "/src/api/service"; +import _ from "lodash-es"; +const apiPrefix = "/pi/plugin"; + +const defaultInputDefine = { + component: { + name: "a-input", + vModel: "modelValue" + } +}; + +export async function GetList(query) { + const plugins = await request({ + url: apiPrefix + "/list", + method: "post", + params: query + }); + for (const plugin of plugins) { + for (const key in plugin.input) { + const field = _.merge({}, defaultInputDefine, plugin.input[key]); + if (field.component.name === "a-input" || field.component.name === "a-select") { + field.component.vModel = "value"; + } + //嵌套对象 + field.key = ["input", key]; + if (field.required) { + delete field.required; + if (field.rules == null) { + field.rules = []; + } + field.rules.push({ required: true, message: "此项必填" }); + } + plugin.input[key] = field; + } + } + console.log("plugins", plugins); + return plugins; +} diff --git a/packages/ui/certd-client/src/views/certd/pipeline/api.ts b/packages/ui/certd-client/src/views/certd/pipeline/api.ts new file mode 100644 index 000000000..4d564c4be --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/api.ts @@ -0,0 +1,66 @@ +import { request } from "/src/api/service"; +const apiPrefix = "/pi/pipeline"; + +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "post", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "post", + params: { id } + }); +} + +export function GetDetail(id) { + return request({ + url: apiPrefix + "/detail", + method: "post", + params: { id } + }); +} + +export function Save(pipelineEntity) { + return request({ + url: apiPrefix + "/save", + method: "post", + data: pipelineEntity + }); +} + +export function Trigger(id) { + return request({ + url: apiPrefix + "/trigger", + method: "post", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/crud.jsx b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/crud.jsx new file mode 100644 index 000000000..4a2b35752 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/crud.jsx @@ -0,0 +1,129 @@ +import { compute } from "@fast-crud/fast-crud"; +import { Dicts } from "./dicts"; + +export default function () { + return { + crudOptions: { + form: { + wrapper: { + width: "1150px" + } + }, + columns: { + domains: { + title: "域名", + type: ["dict-select"], + search: { + show: true, + component: { + name: "a-input" + } + }, + form: { + col: { + span: 24 + }, + wrapperCol: { + span: null + }, + component: { + mode: "tags", + open: false + }, + helper: { + render: () => { + return ( +
+
支持通配符域名,例如: *.foo.com 、 *.test.handsfree.work
+
支持多个域名、多个子域名、多个通配符域名打到一个证书上(域名必须是在同一个DNS提供商解析)
+
多级子域名要分成多个域名输入(*.foo.com的证书不能用于xxx.yyy.foo.com)
+
输入一个回车之后,再输入下一个
+
+ ); + } + }, + valueResolve({ form }) { + if (form.domains instanceof String) { + form.domains = form.domains?.join(","); + } + }, + rules: [{ required: true, message: "请填写域名" }] + } + }, + email: { + title: "邮箱", + type: "text", + search: { show: false }, + form: { + rules: [{ required: true, type: "email", message: "请填写邮箱" }] + } + }, + dnsProviderType: { + title: "DNS提供商", + type: "dict-select", + dict: Dicts.dnsProviderTypeDict, + form: { + value: "aliyun", + rules: [{ required: true, message: "请选择DNS提供商" }], + valueChange({ form }) { + form.dnsProviderAccess = null; + } + } + }, + dnsProviderAccess: { + title: "DNS授权", + type: "text", + form: { + component: { + name: "PiAccessSelector", + type: compute(({ form }) => { + return form.dnsProviderType; + }), + vModel: "modelValue" + }, + rules: [{ required: true, message: "请选择DNS授权" }] + } + } + // country: { + // title: "国家", + // type: "text", + // form: { + // value: "China" + // } + // }, + // state: { + // title: "省份", + // type: "text", + // form: { + // value: "GuangDong" + // } + // }, + // locality: { + // title: "市区", + // type: "text", + // form: { + // value: "NanShan" + // } + // }, + // organization: { + // title: "单位", + // type: "text", + // form: { + // value: "CertD" + // } + // }, + // organizationUnit: { + // title: "部门", + // type: "text", + // form: { + // value: "IT Dept" + // } + // }, + // remark: { + // title: "备注", + // type: "text" + // } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/dicts.ts b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/dicts.ts new file mode 100644 index 000000000..6a5d402df --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/dicts.ts @@ -0,0 +1,9 @@ +import { dict } from "@fast-crud/fast-crud"; + +export const Dicts = { + certIssuerDict: dict({ data: [{ value: "letencrypt", label: "LetEncrypt" }] }), + challengeTypeDict: dict({ data: [{ value: "dns", label: "DNS校验" }] }), + dnsProviderTypeDict: dict({ + url: "pi/dnsProvider/dnsProviderTypeDict" + }) +}; diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/index.vue new file mode 100644 index 000000000..81cd96250 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/index.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx new file mode 100644 index 000000000..19f6630fe --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx @@ -0,0 +1,225 @@ +import * as api from "./api"; +import { useI18n } from "vue-i18n"; +import { ref, shallowRef } from "vue"; +import { useRouter } from "vue-router"; +import { dict } from "@fast-crud/fast-crud"; +import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status"; +import { nanoid } from "nanoid"; +import { message } from "ant-design-vue"; +export default function ({ expose, certdFormRef }) { + const router = useRouter(); + const { t } = useI18n(); + const lastResRef = ref(); + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + const res = await api.UpdateObj(form); + lastResRef.value = res; + return res; + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + form.content = JSON.stringify({ + title: form.title + }); + const res = await api.AddObj(form); + lastResRef.value = res; + return res; + }; + function addCertdPipeline() { + certdFormRef.value.open(async ({ form }) => { + // 添加certd pipeline + const pipeline = { + title: form.domains[0] + "证书自动化", + stages: [ + { + id: nanoid(), + title: "证书申请阶段", + tasks: [ + { + id: nanoid(), + title: "证书申请任务", + steps: [ + { + id: nanoid(), + title: "申请证书", + input: { + renewDays: 20, + ...form + }, + strategy: { + runStrategy: 0 // 正常执行 + }, + type: "CertApply" + } + ] + } + ] + } + ] + }; + + const id = await api.Save({ + content: JSON.stringify(pipeline), + keepHistoryCount: 30 + }); + message.success("创建成功,请添加证书部署任务"); + router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } }); + }); + } + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + actionbar: { + buttons: { + add: { + order: 5, + text: "自定义流水线" + }, + addCertd: { + order: 1, + text: "添加证书流水线", + type: "primary", + click() { + addCertdPipeline(); + } + } + } + }, + form: { + afterSubmit({ form, res, mode }) { + if (mode === "add") { + router.push({ path: "/certd/pipeline/detail", query: { id: res.id, editMode: "true" } }); + } + } + }, + rowHandle: { + buttons: { + view: { + click({ row }) { + router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "false" } }); + } + }, + config: { + order: 1, + title: null, + type: "link", + icon: "ant-design:edit-outlined", + click({ row }) { + router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "true" } }); + } + }, + edit: { + order: 2, + icon: "ant-design:setting-outlined" + }, + remove: { + order: 5 + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + title: { + title: "流水线名称", + type: "text", + search: { + show: true, + component: { + name: "a-input" + } + }, + column: { + width: 300 + } + }, + lastHistoryTime: { + title: "最后运行", + type: "datetime", + form: { + show: false + } + }, + status: { + title: "状态", + type: "dict-select", + dict: dict({ + data: statusUtil.getOptions() + }), + form: { + show: false + } + }, + + disabled: { + title: "启用", + type: "dict-switch", + dict: dict({ + data: [ + { value: true, label: "禁用" }, + { value: false, label: "启用" } + ] + }), + form: { + value: false, + show: false + }, + column: { + component: { + name: "fs-dict-switch", + vModel: "checked" + }, + async valueChange({ row, key, value }) { + return await api.UpdateObj({ + id: row.id, + disabled: row[key] + }); + } + } + }, + keepHistoryCount: { + title: "历史记录保持数", + type: "number", + form: { + value: 30, + helper: "历史记录保持条数,多余的会被删除" + } + }, + createTime: { + title: "创建时间", + type: "datetime", + form: { + show: false + } + }, + updateTime: { + title: "更新时间", + type: "datetime", + form: { + show: false + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/certd/pipeline/dash-roll.readme b/packages/ui/certd-client/src/views/certd/pipeline/dash-roll.readme new file mode 100644 index 000000000..df3bbf8ba --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/dash-roll.readme @@ -0,0 +1 @@ +https://stackoverflow.com/questions/28365839/dashed-border-animation-in-css3-animation \ No newline at end of file diff --git a/packages/ui/certd-client/src/views/certd/pipeline/detail.vue b/packages/ui/certd-client/src/views/certd/pipeline/detail.vue new file mode 100644 index 000000000..31065ae47 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/detail.vue @@ -0,0 +1,86 @@ + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/index.vue new file mode 100644 index 000000000..be9f15943 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/index.vue @@ -0,0 +1,52 @@ + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/history-timeline-item.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/history-timeline-item.vue new file mode 100644 index 000000000..c27265f6e --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/history-timeline-item.vue @@ -0,0 +1,66 @@ + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/output-selector/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/output-selector/index.vue new file mode 100644 index 000000000..fbef2421f --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/output-selector/index.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/status-show.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/status-show.vue new file mode 100644 index 000000000..0f27d8d89 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/status-show.vue @@ -0,0 +1,46 @@ + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/step-form/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/step-form/index.vue new file mode 100644 index 000000000..433aaee18 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/step-form/index.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-form/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-form/index.vue new file mode 100644 index 000000000..e5d847b93 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-form/index.vue @@ -0,0 +1,265 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-view/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-view/index.vue new file mode 100644 index 000000000..5733cabdf --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/task-view/index.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/trigger-form/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/trigger-form/index.vue new file mode 100644 index 000000000..5d1c4800a --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/trigger-form/index.vue @@ -0,0 +1,208 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue new file mode 100644 index 000000000..8d731d888 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue @@ -0,0 +1,652 @@ + + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/plugin/index.ts b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/plugin/index.ts new file mode 100644 index 000000000..07efe368c --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/plugin/index.ts @@ -0,0 +1,65 @@ +export class PluginManager { + // @ts-ignore + map: { + [key: string]: any; + } = {}; + + /** + * 初始化plugins + * @param plugins + */ + init(plugins) { + const list = plugins; + const map = {}; + for (const plugin of list) { + map[plugin.key] = plugin; + } + this.map = map; + } + + get(name: string) { + return this.map[name]; + } + + getPreStepOutputOptions({ pipeline, currentStageIndex, currentStepIndex, currentTask }) { + const steps = this.collectionPreStepOutputs({ + pipeline, + currentStageIndex, + currentStepIndex, + currentTask + }); + const options: any[] = []; + for (const step of steps) { + const stepDefine = this.get(step.type); + for (const key in stepDefine?.output) { + options.push({ + value: `step.${step.id}.${key}`, + label: `${stepDefine.output[key].title}【from:${step.title}】`, + type: step.type + }); + } + } + return options; + } + + collectionPreStepOutputs({ pipeline, currentStageIndex, currentStepIndex, currentTask }) { + const steps: any[] = []; + // 开始放step + for (let i = 0; i < currentStageIndex; i++) { + const stage = pipeline.stages[i]; + for (const task of stage.tasks) { + for (const step of task.steps) { + steps.push(step); + } + } + } + //放当前任务下的step + for (let i = 0; i < currentStepIndex; i++) { + const step = currentTask.steps[i]; + steps.push(step); + } + return steps; + } +} + +export const pluginManager = new PluginManager(); diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/type.d.ts b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/type.d.ts new file mode 100644 index 000000000..952304a61 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/type.d.ts @@ -0,0 +1,22 @@ +import { PluginDefine, Pipeline } from "@certd/pipeline/src"; +export * from "@certd/pipeline/src"; +export type PipelineDetail = { + pipeline: Pipeline; +}; + +export type RunHistory = { + id: any; + pipeline: Pipeline; + logs?: { + [id: string]: string[]; + }; +}; + +export type PipelineOptions = { + doTrigger(options: { pipelineId }): Promise; + doSave(pipelineConfig: PipelineDefile): Promise; + getPipelineDetail(query: { pipelineId }): Promise; + getHistoryList(query: { pipelineId }): Promise; + getHistoryDetail(query: { historyId }): Promise; + getPlugins(): Promise; +}; diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/utils/util.status.ts b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/utils/util.status.ts new file mode 100644 index 000000000..429be4015 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/utils/util.status.ts @@ -0,0 +1,49 @@ +const StatusEnum = { + success: { + value: "success", + label: "成功", + color: "green", + icon: "ant-design:check-circle-outlined" + }, + error: { + value: "error", + label: "错误", + color: "red", + icon: "ant-design:info-circle-outlined" + }, + skip: { + value: "skip", + label: "跳过", + color: "blue", + icon: "fluent:arrow-step-over-20-filled" + }, + start: { + value: "start", + label: "运行中", + color: "blue", + spin: true, + icon: "ant-design:sync-outlined" + }, + none: { + value: "none", + label: "未运行", + color: "blue", + icon: "ant-design:minus-circle-twotone" + } +}; +export const statusUtil = { + getColor(status = "none") { + return StatusEnum[status].color; + }, + get(status = "none") { + return StatusEnum[status]; + }, + + getOptions() { + const options: any[] = []; + for (const key of Object.keys(StatusEnum)) { + options.push(StatusEnum[key]); + } + return options; + } +}; diff --git a/packages/ui/certd-client/src/views/crud/advanced/big-data/api.js b/packages/ui/certd-client/src/views/crud/advanced/big-data/api.js new file mode 100644 index 000000000..a2e187ae2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/big-data/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/AdvancedBigData"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/big-data/crud.jsx b/packages/ui/certd-client/src/views/crud/advanced/big-data/crud.jsx new file mode 100644 index 000000000..d66f65d81 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/big-data/crud.jsx @@ -0,0 +1,170 @@ +import * as api from "./api"; +import { message } from "ant-design-vue"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + output: {}, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + scroll: { + //启用横向滚动条,设置一个大于所有列宽之和的值,一般大于表格宽度 + x: 2400 + } + }, + pagination: { + pageSize: 100 + }, + rowHandle: { + fixed: "right" + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + text: { + title: "文本", + type: "text" + }, + dict1: { + title: "字典1", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict1" + }) + }, + dict2: { + title: "字典2", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict2" + }) + }, + dict3: { + title: "字典3", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict3" + }) + }, + dict4: { + title: "字典4", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict4" + }) + }, + dict5: { + title: "字典5", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict5" + }) + }, + dict6: { + title: "字典6", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict6" + }) + }, + dict7: { + title: "字典7", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict7" + }) + }, + dict8: { + title: "字典8", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict8" + }) + }, + dict9: { + title: "字典9", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict9" + }) + }, + dict10: { + title: "字典10", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?from=dict10" + }) + }, + text1: { + title: "文本", + type: "text" + }, + text2: { + title: "文本", + type: "text" + }, + text3: { + title: "文本", + type: "text" + }, + text4: { + title: "文本", + type: "text" + }, + text5: { + title: "文本", + type: "text" + }, + text6: { + title: "文本", + type: "text" + }, + text7: { + title: "文本", + type: "text" + }, + text8: { + title: "文本", + type: "text" + }, + text9: { + title: "文本", + type: "text" + }, + text10: { + title: "文本", + type: "text" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/big-data/index.vue b/packages/ui/certd-client/src/views/crud/advanced/big-data/index.vue new file mode 100644 index 000000000..91a4914c0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/big-data/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/big-data/mock.js b/packages/ui/certd-client/src/views/crud/advanced/big-data/mock.js new file mode 100644 index 000000000..a166ac9d3 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/big-data/mock.js @@ -0,0 +1,126 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "AdvancedBigData", + idGenerator: 0, + copyTimes: 1000 +}; +const list = [ + { + text: "测试文本", + dict1: "1", + dict2: "1", + dict3: "2", + dict4: "1", + dict5: "2", + dict6: "1", + dict7: "1", + dict8: "1", + text1: "测试文本1", + text2: "测试文本2", + text3: "测试文本3", + text4: "测试文本4", + text5: "测试文本5", + text6: "测试文本6", + text7: "测试文本7", + text8: "测试文本8", + dict9: "2", + dict10: "1", + dict11: "2", + dict12: "1" + }, + { + text: "测试文本", + dict1: "1", + dict2: "1", + dict3: "2", + dict4: "1", + dict5: "2", + dict6: "1", + dict7: "1", + dict8: "1", + text1: "测试文本1", + text2: "测试文本2", + text3: "测试文本3", + text4: "测试文本4", + text5: "测试文本5", + text6: "测试文本6", + text7: "测试文本7", + text8: "测试文本8", + dict9: "2", + dict10: "1", + dict11: "2", + dict12: "1" + }, + { + text: "测试文本", + dict1: "1", + dict2: "1", + dict3: "2", + dict4: "1", + dict5: "2", + dict6: "1", + dict7: "1", + dict8: "1", + text1: "测试文本1", + text2: "测试文本2", + text3: "测试文本3", + text4: "测试文本4", + text5: "测试文本5", + text6: "测试文本6", + text7: "测试文本7", + text8: "测试文本8", + dict9: "2", + dict10: "1", + dict11: "2", + dict12: "1" + }, + { + text: "测试文本", + dict1: "1", + dict2: "1", + dict3: "2", + dict4: "1", + dict5: "2", + dict6: "1", + dict7: "1", + dict8: "1", + text1: "测试文本1", + text2: "测试文本2", + text3: "测试文本3", + text4: "测试文本4", + text5: "测试文本5", + text6: "测试文本6", + text7: "测试文本7", + text8: "测试文本8", + dict9: "2", + dict10: "1", + dict11: "2", + dict12: "1" + }, + { + text: "测试文本", + dict1: "1", + dict2: "1", + dict3: "2", + dict4: "1", + dict5: "2", + dict6: "1", + dict7: "1", + dict8: "1", + text1: "测试文本1", + text2: "测试文本2", + text3: "测试文本3", + text4: "测试文本4", + text5: "测试文本5", + text6: "测试文本6", + text7: "测试文本7", + text8: "测试文本8", + dict9: "2", + dict10: "1", + dict11: "2", + dict12: "1" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/advanced/from-backend/api.js b/packages/ui/certd-client/src/views/crud/advanced/from-backend/api.js new file mode 100644 index 000000000..ae38d2931 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/from-backend/api.js @@ -0,0 +1,48 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/AdvancedFromBackend"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} +export function GetCrud() { + return request({ + url: apiPrefix + "/crud", + method: "get" + }); +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/from-backend/crud-backend.js b/packages/ui/certd-client/src/views/crud/advanced/from-backend/crud-backend.js new file mode 100644 index 000000000..abcf8ef11 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/from-backend/crud-backend.js @@ -0,0 +1,29 @@ +export const crudOptions = ` + ({expose,dict}) => { + return { + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } +} + + +`; diff --git a/packages/ui/certd-client/src/views/crud/advanced/from-backend/crud.jsx b/packages/ui/certd-client/src/views/crud/advanced/from-backend/crud.jsx new file mode 100644 index 000000000..a577b88d8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/from-backend/crud.jsx @@ -0,0 +1,27 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/from-backend/index.vue b/packages/ui/certd-client/src/views/crud/advanced/from-backend/index.vue new file mode 100644 index 000000000..bec759306 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/from-backend/index.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/from-backend/mock.js b/packages/ui/certd-client/src/views/crud/advanced/from-backend/mock.js new file mode 100644 index 000000000..2f802d45d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/from-backend/mock.js @@ -0,0 +1,35 @@ +import mockUtil from "/src/mock/base"; +import { crudOptions } from "./crud-backend"; +const options = { + name: "AdvancedFromBackend", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); + +mock.push({ + path: "/AdvancedFromBackend/crud", + method: "get", + handle(req) { + return { + code: 0, + msg: "success", + data: crudOptions + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/api.js b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/api.js new file mode 100644 index 000000000..236e922ac --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/AdvancedInDialog"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/crud.jsx b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/crud.jsx new file mode 100644 index 000000000..cde2c0c2f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/crud.jsx @@ -0,0 +1,106 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + name: { + title: "姓名", + type: "text", //虽然不写也能正确显示组件,但不建议省略它 + search: { show: true }, + form: { + component: { + maxlength: 20 + } + } + }, + search: { + title: "搜索", + type: "text", + form: { + component: { + addonAfter: "后置", + suffix: "suffix", + children: { + addonBefore() { + return ; + } + } + } + } + }, + password: { + title: "密码", + type: "password", + column: { + //一般密码不显示在列里面 + show: false + } + }, + intro: { + title: "简介", + type: "textarea", + form: { + component: { showWordLimit: true, maxlength: 200 } + }, + column: { + ellipsis: true + } + }, + render: { + title: "复杂输入(render)", + form: { + title: "复杂输入", + component: { + render(context) { + console.log("context scope", context); + return ( + + + + + ); + } + } + } + }, + render2: { + title: "我的值是由复杂输入列输入的", + column: { + width: "300px" + }, + form: { + show: false + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/index.vue b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/index.vue new file mode 100644 index 000000000..5b3591efe --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/mock.js b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/mock.js new file mode 100644 index 000000000..31a5c5618 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/crud/mock.js @@ -0,0 +1,40 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "AdvancedInDialog", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + date: "2016-05-02", + status: "0", + province: "1", + avatar: "https://alicdn.antdv.com/vue.png", + show: true, + city: "sz", + address: "123123", + zip: "518000", + intro: "王小虎是element-plus的table示例出现的名字" + }, + { + name: "张三", + date: "2016-05-04", + status: "1", + province: "2" + }, + { + name: "李四", + date: 2232433534511, + status: "1", + province: "0" + }, + { + name: "王五", + date: "2016-05-03", + status: "2", + province: "wh,gz" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/advanced/in-dialog/index.vue b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/index.vue new file mode 100644 index 000000000..64ac77137 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/in-dialog/index.vue @@ -0,0 +1,37 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/linkage/api.js b/packages/ui/certd-client/src/views/crud/advanced/linkage/api.js new file mode 100644 index 000000000..bbc69cc2f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/linkage/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormLinkage"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/linkage/crud.jsx b/packages/ui/certd-client/src/views/crud/advanced/linkage/crud.jsx new file mode 100644 index 000000000..6fe185407 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/linkage/crud.jsx @@ -0,0 +1,118 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + align: "center" + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + province: { + title: "省", + type: "dict-select", + search: { + show: true + }, + dict: dict({ + url: "/mock/linkage/province", + value: "id", + cache: true + }), + form: { + valueChange({ form, value, getComponentRef }) { + form.city = undefined; // 将“city”的值置空 + form.county = undefined; // 将“county”的值置空 + if (value) { + getComponentRef("city").reloadDict(); // 执行city的select组件的reloadDict()方法,触发“city”重新加载字典 + } + } + } + }, + city: { + title: "市", + type: "dict-select", + search: { + show: true + }, + dict: dict({ + cache: true, + prototype: true, + // url() 改成构建url,返回一个url + url({ form }) { + if (form && form.province != null) { + // 本数据字典的url是通过前一个select的选项决定的 + return `/mock/linkage/city?province=${form.province}`; + } + return undefined; // 返回undefined 将不加载字典 + }, + value: "id" + }), + form: { + // 注释同上 + valueChange({ value, form, getComponentRef }) { + if (value) { + form.county = undefined; // 将county的value置空 + const countySelect = getComponentRef("county"); + if (form && form.province && form.city) { + countySelect.reloadDict(); // 重新加载字典项 + } else { + countySelect.clearDict(); // 清空选项 + } + } + } + } + }, + county: { + title: "区", + type: "dict-select", + search: { + show: true + }, + dict: dict({ + value: "id", + cache: true, + prototype: true, + url({ form }) { + if (form && form.province != null && form.city != null) { + return `/mock/linkage/county?province=${form.province} &city=${form.city}`; + } + return undefined; + } + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/linkage/index.vue b/packages/ui/certd-client/src/views/crud/advanced/linkage/index.vue new file mode 100644 index 000000000..48faf05b2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/linkage/index.vue @@ -0,0 +1,38 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/linkage/mock.js b/packages/ui/certd-client/src/views/crud/advanced/linkage/mock.js new file mode 100644 index 000000000..8e42b2d52 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/linkage/mock.js @@ -0,0 +1,129 @@ +import mockUtil from "/src/mock/base"; +import _ from "lodash-es"; +const options = { + name: "FormLinkage", + idGenerator: 0 +}; +const list = [ + { + province: 10000, + city: 100003, + county: 100004 + }, + { + province: 10010, + city: 100113, + county: 100115 + } +]; +const tree = [ + { + id: 10000, + label: "北京市", + children: [ + { + id: 100003, + label: "北京市辖区", + children: [ + { id: 100004, label: "东城区" }, + { id: 100005, label: "西城区" } + ] + }, + { + id: 100103, + label: "北京郊区", + children: [ + { id: 100104, label: "东郊" }, + { id: 100105, label: "西郊" } + ] + } + ] + }, + { + id: 10010, + label: "天津市", + children: [ + { + id: 100013, + label: "天津市辖区", + children: [ + { id: 100014, label: "天津湾" }, + { id: 100015, label: "渤海湾" } + ] + }, + { + id: 100113, + label: "天津市郊区", + children: [ + { id: 100114, label: "天津湾郊区" }, + { id: 100115, label: "渤海湾郊区" } + ] + } + ] + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); + +function omitChildren(orignalListt) { + const list = []; + orignalListt.forEach((item) => { + list.push(_.omit(item, "children")); + }); + return list; +} +mock.push({ + path: "/mock/linkage/province", + method: "get", + handle() { + const list = omitChildren(tree); + return { + code: 0, + msg: "success", + data: list + }; + } +}); + +mock.push({ + path: "/mock/linkage/city", + method: "get", + handle(req) { + const province = parseInt(req.params.province); + const a = tree.filter((item) => { + return item.id === province; + }); + const list = omitChildren(a[0].children); + return { + code: 0, + msg: "success", + data: list + }; + } +}); + +mock.push({ + path: "/mock/linkage/county", + method: "get", + handle(req) { + const province = parseInt(req.params.province); + const a = tree.filter((item) => { + return item.id === province; + }); + const city = parseInt(req.params.city); + const b = a[0].children.filter((item) => { + return item.id === city; + }); + + const list = omitChildren(b[0].children); + return { + code: 0, + msg: "success", + data: list + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/advanced/local-pagination/api.js b/packages/ui/certd-client/src/views/crud/advanced/local-pagination/api.js new file mode 100644 index 000000000..457dac6d8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/local-pagination/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/AdvancedLocalPagination"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/local-pagination/crud.jsx b/packages/ui/certd-client/src/views/crud/advanced/local-pagination/crud.jsx new file mode 100644 index 000000000..e4989bee1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/local-pagination/crud.jsx @@ -0,0 +1,101 @@ +import * as api from "./api"; +import _ from "lodash-es"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose, localDataRef }) { + const pageRequest = async ({ page, query }) => { + //总数据 + let data = localDataRef.value; + //获取请求参数 + const limit = page.limit; + let offset = page.offset; + data = data.filter((item) => { + // 根据你的业务,编写你的本地查询逻辑 + // text改成你的查询字段 + if (query.status && item.status !== query.status) { + return false; + } + return true; + }); + + // 本地分页 + const start = offset; + let end = offset + limit; + if (data.length < end) { + end = data.length; + } + const records = data.slice(start, end); + + // 构造返回结果 + return { + offset, + limit, + total: localDataRef.value.length, + records + }; + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + await api.UpdateObj(form); + //更新本地数据 + const tableData = localDataRef.value; + for (const item of tableData) { + if (item.id === form.id) { + _.merge(item, form); + } + } + }; + + const addRequest = async ({ form }) => { + const id = await api.AddObj(form); + //本地添加 + form.id = id; + localDataRef.value.unshift(form); + return id; + }; + + const delRequest = async ({ row }) => { + await api.DelObj(row.id); + //本地删除那一条记录 + const tableData = localDataRef.value; + let index = 0; + for (const item of tableData) { + if (item.id === row.id) { + localDataRef.value.splice(index, 1); + } + index++; + } + }; + + return { + output: {}, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + status: { + title: "状态", + search: { show: true }, + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/local-pagination/index.vue b/packages/ui/certd-client/src/views/crud/advanced/local-pagination/index.vue new file mode 100644 index 000000000..d48a680ee --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/local-pagination/index.vue @@ -0,0 +1,57 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/local-pagination/mock.js b/packages/ui/certd-client/src/views/crud/advanced/local-pagination/mock.js new file mode 100644 index 000000000..120c2a634 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/local-pagination/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "AdvancedLocalPagination", + idGenerator: 0 +}; +const list = [ + { + status: "1" + }, + { + status: "2" + }, + { + status: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/api.js b/packages/ui/certd-client/src/views/crud/advanced/nest/api.js new file mode 100644 index 000000000..b18db901d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/AdvancedNest"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/api.js b/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/api.js new file mode 100644 index 000000000..3a8a4a1b2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/AdvancedAside"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/crud.jsx b/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/crud.jsx new file mode 100644 index 000000000..567715c65 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/crud.jsx @@ -0,0 +1,56 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + pagination: { + showSizeChanger: false, // antdv + showQuickJumper: false // antdv + }, + request: { + pageRequest: api.GetList, + addRequest, + editRequest, + delRequest + }, + toolbar: { + compact: false + }, + rowHandle: { + width: "230px" + }, + table: {}, + columns: { + gradeId: { + title: "年级Id", + search: { show: true }, + type: "number", + column: { + width: 80, + align: "center", + sortable: true + } + }, + class: { + title: "班级", + search: { show: false }, + type: "text", + column: { + sortable: true + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/index.vue b/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/index.vue new file mode 100644 index 000000000..9a198b9e4 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/mock.js b/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/mock.js new file mode 100644 index 000000000..2d70c579f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/aside-table/mock.js @@ -0,0 +1,26 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "AdvancedAside", + idGenerator: 0 +}; +const list = [ + { + class: "一班", + gradeId: 1 + }, + { + class: "二班", + gradeId: 1 + }, + { + class: "三班", + gradeId: 2 + }, + { + class: "四班", + gradeId: 2 + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/crud.jsx b/packages/ui/certd-client/src/views/crud/advanced/nest/crud.jsx new file mode 100644 index 000000000..b42ba5c82 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/crud.jsx @@ -0,0 +1,101 @@ +import * as api from "./api"; +import { ref, shallowRef } from "vue"; +import SubTable from "./sub-table/index.vue"; +import { compute } from "@fast-crud/fast-crud"; +export default function ({ expose, asideTableRef }) { + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const currentRow = ref(); + + const onCurrentRowChange = (id) => { + currentRow.value = id; + asideTableRef.value.setSearchFormData({ form: { gradeId: id } }); + asideTableRef.value.doRefresh(); + }; + return { + crudOptions: { + table: { + customRow(record, index) { + const clazz = record.id === currentRow.value ? "fs-current-row" : ""; + return { + onClick() { + onCurrentRowChange(record.id); + }, + class: clazz + }; + } + }, + pagination: { + showSizeChanger: false, // antdv + showQuickJumper: false // antdv + }, + form: { + wrapper: { + is: "a-drawer" + } + }, + request: { + pageRequest: api.GetList, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + width: "240px" + }, + toolbar: { + compact: false + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + grade: { + title: "年级", + search: { show: true }, + type: "text", + column: { + sortable: true + } + }, + nestId: { + title: "嵌套表格", + //复合字段类型 + type: ["number", "colspan"], + form: { + // 嵌套表格字段 + rules: [{ required: true, message: "请选择用户" }], + component: { + //局部引用子表格,要用shallowRef包裹 + name: shallowRef(SubTable), + vModel: "modelValue", + gradeId: compute(({ form }) => { + return form.id; + }) + } + // antdv 的跨列配置,需要配置如下三个, 可以通过colspan简化 + // col: { span: 24 }, + // labelCol: { span: 2 }, + // wrapperCol: { span: 21 } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/index.vue b/packages/ui/certd-client/src/views/crud/advanced/nest/index.vue new file mode 100644 index 000000000..5fa4b445e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/index.vue @@ -0,0 +1,59 @@ + + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/mock.js b/packages/ui/certd-client/src/views/crud/advanced/nest/mock.js new file mode 100644 index 000000000..709699d22 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/mock.js @@ -0,0 +1,22 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "AdvancedNest", + idGenerator: 0 +}; +const list = [ + { + grade: "一年级", + nestId: 1 + }, + { + grade: "二年级", + nestId: 2 + }, + { + grade: "三年级", + nestId: 3 + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/api.js b/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/api.js new file mode 100644 index 000000000..167a31de3 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/AdvancedSubTable"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/crud.jsx b/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/crud.jsx new file mode 100644 index 000000000..ad5faa1e5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/crud.jsx @@ -0,0 +1,62 @@ +import * as api from "./api"; +export default function ({ expose, props, ctx }) { + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + table: { + customRow(record, index) { + const clazz = record.id === props.modelValue ? "fs-current-row" : ""; + return { + onClick() { + ctx.emit("update:modelValue", record.id); + }, + class: clazz + }; + } + }, + request: { + pageRequest: api.GetList, + addRequest, + editRequest, + delRequest + }, + search: { show: false }, + form: { + wrapper: { + is: "a-drawer" + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + name: { + title: "用户姓名", + search: { show: true }, + type: "text", + column: { + sortable: true + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/index.vue b/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/index.vue new file mode 100644 index 000000000..fb9284386 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/index.vue @@ -0,0 +1,61 @@ + + + + diff --git a/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/mock.js b/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/mock.js new file mode 100644 index 000000000..726a628a8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/advanced/nest/sub-table/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "AdvancedSubTable", + idGenerator: 0 +}; +const list = [ + { + name: "张三" + }, + { + name: "李四" + }, + { + name: "王五" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/basis/columns-set/api.js b/packages/ui/certd-client/src/views/crud/basis/columns-set/api.js new file mode 100644 index 000000000..2d20318fa --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/columns-set/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/BasisColumnsSet"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/basis/columns-set/crud.jsx b/packages/ui/certd-client/src/views/crud/basis/columns-set/crud.jsx new file mode 100644 index 000000000..559e49e35 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/columns-set/crud.jsx @@ -0,0 +1,69 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + toolbar: { + columnsFilter: { + mode: "default" + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + }, + disabled: { + title: "列设置禁用", + type: "text", + column: { + columnSetDisabled: true + } + }, + hidden: { + title: "列设置隐藏", + type: "text", + column: { + columnSetShow: false + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/basis/columns-set/index.vue b/packages/ui/certd-client/src/views/crud/basis/columns-set/index.vue new file mode 100644 index 000000000..a54361075 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/columns-set/index.vue @@ -0,0 +1,52 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/columns-set/mock.js b/packages/ui/certd-client/src/views/crud/basis/columns-set/mock.js new file mode 100644 index 000000000..90154b73b --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/columns-set/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "BasisColumnsSet", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/basis/compute-more/api.js b/packages/ui/certd-client/src/views/crud/basis/compute-more/api.js new file mode 100644 index 000000000..39c56ba12 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/compute-more/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormComputeMore"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/basis/compute-more/crud.jsx b/packages/ui/certd-client/src/views/crud/basis/compute-more/crud.jsx new file mode 100644 index 000000000..92ccdee89 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/compute-more/crud.jsx @@ -0,0 +1,79 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { useCompute } from "@fast-crud/fast-crud"; +import { message } from "ant-design-vue"; +import { ref, computed } from "vue"; +const { asyncCompute, compute } = useCompute(); +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + //普通的ref引用,可以动态切换配置 + const defValueRef = ref("我是动态的默认值"); + const defValueComputed = computed(() => { + return defValueRef.value; + }); + return { + output: { + defValueRef, + defValueComputed + }, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + scroll: { + x: 1500 + } + }, + form: { + labelCol: { span: 8 }, + wrapperCol: { span: 14 } + }, + rowHandle: { + fixed: "right", + align:'center', + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + defValue: { + title: "默认值", + type: "text", + search: { show: true, value: null }, + form: { + // form.value不支持asyncCompute + // 假如你的默认值异步获取的,那么你自己必须保证先异步计算完成之后,才能打开对话框。 + // 因为在打开对话框时,默认值就必须得设置好。 + value: defValueRef + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/basis/compute-more/index.vue b/packages/ui/certd-client/src/views/crud/basis/compute-more/index.vue new file mode 100644 index 000000000..01d9da383 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/compute-more/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/compute-more/mock.js b/packages/ui/certd-client/src/views/crud/basis/compute-more/mock.js new file mode 100644 index 000000000..ee602acfa --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/compute-more/mock.js @@ -0,0 +1,32 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FormComputeMore", + idGenerator: 0 +}; +const list = [ + { + ref: "根据showRef显示", + compute: true, + status: "1", + remote: "2", + shower: "---> 点右边编辑查看示例效果", + remote2: "2", + editable: true + }, + { + compute: false, + status: "2", + remote: "0", + remote2: "2", + editable: false + }, + { + compute: true, + status: "0", + remote2: "2", + editable: true + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/basis/compute/api.js b/packages/ui/certd-client/src/views/crud/basis/compute/api.js new file mode 100644 index 000000000..c8ec9fc8e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/compute/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormCompute"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/basis/compute/crud.jsx b/packages/ui/certd-client/src/views/crud/basis/compute/crud.jsx new file mode 100644 index 000000000..0f450f138 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/compute/crud.jsx @@ -0,0 +1,211 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { useCompute } from "@fast-crud/fast-crud"; +import { message } from "ant-design-vue"; +import { ref, computed } from "vue"; +const { asyncCompute, compute } = useCompute(); +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + //普通的ref引用,可以动态切换配置 + const showRef = ref(false); + const showTableRef = ref(true); + const showTableComputed = computed(() => { + return showTableRef.value; + }); + + const columnComponentShowRef = ref(true); + const columnComponentShowComputed = computed(() => { + return columnComponentShowRef.value; + }); + + return { + output: { + showRef, + showTableRef, + columnComponentShowRef + }, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + scroll: { + x: 1500 + }, + //通过switch动态显隐table + show: showTableComputed //不仅支持computed,直接传showTableRef也是可以的 + }, + form: { + labelCol: { span: 8 }, + wrapperCol: { span: 14 } + }, + rowHandle: { + fixed: "right", + buttons: { + edit: { + show: compute(({ row }) => { + return row.editable; + }) + }, + remove: { + show: compute(({ row }) => { + return row.editable; + }) + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50, + resizable: true + }, + form: { + show: false + } + }, + refSwitch: { + title: "ref引用切换", + type: "text", + form: { + helper: "点我切换右边的输入框显示" + } + }, + ref: { + title: "根据ref引用显示", + type: ["text"], + form: { + component: { + show: showRef + }, + helper: "我会根据showRef进行显隐" + } + }, + compute: { + title: "compute", + search: { show: false }, + type: "text", + column: { + show: columnComponentShowComputed, + columnSetDisabled: true, //这里采用自定义控制显隐,那么列设置里面就要禁用 + // columnSetShow: false, //直接不在列设置里面显示也行 + component: { + name: "a-switch", + vModel: "checked" + } + }, + form: { + component: { + name: "a-switch", + vModel: "checked" + }, + helper: "点我触发动态计算" + } + }, + shower: { + title: "根据compute显示", + search: { show: false }, + type: "button", + form: { + component: { + show: compute(({ form }) => { + return form.compute; + }) + } + }, + column: { + width: 250, + resizable: true, + component: { + show: compute(({ row }) => { + return row.compute; + }) + } + } + }, + remote: { + title: "asyncCompute", + search: { show: true }, + type: "text", + form: { + component: { + name: "a-select", + vModel: "value", + placeholder: "异步计算远程获取options", + options: asyncCompute({ + async asyncFn(watchValue, context) { + const url = "/mock/dicts/OpenStatusEnum?remote"; + return await requestForMock({ url }); + } + }) + }, + helper: "我的options是异步计算远程获取的,只会获取一次" + } + }, + remote2: { + title: "监听switch触发异步计算", + search: { show: false }, + type: "text", + form: { + component: { + name: "a-select", + vModel: "value", + placeholder: "异步计算远程获取options", + options: asyncCompute({ + watch({ form }) { + return form.compute; + }, + async asyncFn(watchValue) { + message.info("监听switch,触发远程获取options"); + const url = watchValue + ? "/mock/dicts/OpenStatusEnum?remote" + : "/mock/dicts/moreOpenStatusEnum?remote"; + return await requestForMock({ url }); + } + }) + }, + helper: "监听其他属性修改后,触发重新计算" + }, + column: { + width: 200 + } + }, + editable: { + title: "可编辑", + search: { show: false }, + type: "text", + column: { + fixed: "right", + component: { + name: "a-switch", + vModel: "checked" + } + }, + form: { + show: false + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/basis/compute/index.vue b/packages/ui/certd-client/src/views/crud/basis/compute/index.vue new file mode 100644 index 000000000..d7408935e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/compute/index.vue @@ -0,0 +1,60 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/compute/mock.js b/packages/ui/certd-client/src/views/crud/basis/compute/mock.js new file mode 100644 index 000000000..b4c21d853 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/compute/mock.js @@ -0,0 +1,34 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FormCompute", + idGenerator: 0 +}; +const list = [ + { + ref: "根据showRef显示", + compute: true, + status: "1", + remote: "2", + shower: "---> 点右边编辑查看示例效果", + remote2: "2", + editable: true + }, + { + compute: false, + shower: "---> 点右边编辑查看示例效果", + status: "2", + remote: "0", + remote2: "2", + editable: false + }, + { + compute: true, + shower: "---> 点右边编辑查看示例效果", + status: "0", + remote2: "2", + editable: true + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/basis/first/index.vue b/packages/ui/certd-client/src/views/crud/basis/first/index.vue new file mode 100644 index 000000000..6edf3a6d1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/first/index.vue @@ -0,0 +1,109 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/i18n/api.js b/packages/ui/certd-client/src/views/crud/basis/i18n/api.js new file mode 100644 index 000000000..d62b1b729 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/i18n/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/BasisI18n"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/basis/i18n/crud.jsx b/packages/ui/certd-client/src/views/crud/basis/i18n/crud.jsx new file mode 100644 index 000000000..e789ca3e4 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/i18n/crud.jsx @@ -0,0 +1,72 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { useI18n } from "vue-i18n"; +export default function ({ expose }) { + const { t } = useI18n(); + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + name: { + title: t("app.crud.i18n.name"), + type: "text", + search: { show: true } + }, + city: { + title: t("app.crud.i18n.city"), + type: "dict-select", + search: { show: true }, + dict: dict({ + value: "id", + label: "text", + data: [ + { id: "sz", text: "深圳", color: "success" }, + { id: "gz", text: "广州", color: "blue" }, + { id: "bj", text: "北京" }, + { id: "wh", text: "武汉" }, + { id: "sh", text: "上海" } + ] + }) + }, + radio: { + title: t("app.crud.i18n.status"), + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/basis/i18n/index.vue b/packages/ui/certd-client/src/views/crud/basis/i18n/index.vue new file mode 100644 index 000000000..3c50ea0a3 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/i18n/index.vue @@ -0,0 +1,53 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/i18n/mock.js b/packages/ui/certd-client/src/views/crud/basis/i18n/mock.js new file mode 100644 index 000000000..4bbcc95b7 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/i18n/mock.js @@ -0,0 +1,25 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "BasisI18n", + idGenerator: 0 +}; +const list = [ + { + radio: "1", + name: "张三", + city: "sz" + }, + { + radio: "2", + name: "李四", + city: "gz" + }, + { + radio: "0", + name: "王五", + city: "sh" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-card/api.js b/packages/ui/certd-client/src/views/crud/basis/layout-card/api.js new file mode 100644 index 000000000..1ee8f29fa --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-card/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/BasisLayoutCard"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-card/crud.jsx b/packages/ui/certd-client/src/views/crud/basis/layout-card/crud.jsx new file mode 100644 index 000000000..085f48522 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-card/crud.jsx @@ -0,0 +1,73 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ crudExpose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + container: { + is: "fs-layout-card" + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + name: { + title: "姓名", + type: "text", + search: { show: true } + }, + city: { + title: "城市", + type: "dict-select", + search: { show: true }, + dict: dict({ + value: "id", + label: "text", + data: [ + { id: "sz", text: "深圳", color: "success" }, + { id: "gz", text: "广州", color: "blue" }, + { id: "bj", text: "北京" }, + { id: "wh", text: "武汉" }, + { id: "sh", text: "上海" } + ] + }) + }, + radio: { + title: "单选", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-card/index.vue b/packages/ui/certd-client/src/views/crud/basis/layout-card/index.vue new file mode 100644 index 000000000..60597b7e4 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-card/index.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-card/mock.js b/packages/ui/certd-client/src/views/crud/basis/layout-card/mock.js new file mode 100644 index 000000000..417d9601f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-card/mock.js @@ -0,0 +1,25 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "BasisLayoutCard", + idGenerator: 0 +}; +const list = [ + { + radio: "1", + name: "张三", + city: "sz" + }, + { + radio: "2", + name: "李四", + city: "gz" + }, + { + radio: "0", + name: "王五", + city: "sh" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-custom/api.js b/packages/ui/certd-client/src/views/crud/basis/layout-custom/api.js new file mode 100644 index 000000000..f243b4020 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-custom/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/BasisLayoutCustom"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-custom/crud.jsx b/packages/ui/certd-client/src/views/crud/basis/layout-custom/crud.jsx new file mode 100644 index 000000000..851929944 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-custom/crud.jsx @@ -0,0 +1,75 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import CustomLayout from "./custom-layout.vue"; +import { shallowRef } from "vue"; +export default function ({ crudExpose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + container: { + is: shallowRef(CustomLayout) //可以将自定义布局组件全局注册,这里只需要配置name即可 + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + name: { + title: "姓名", + type: "text", + search: { show: true } + }, + city: { + title: "城市", + type: "dict-select", + search: { show: false }, + dict: dict({ + value: "id", + label: "text", + data: [ + { id: "sz", text: "深圳", color: "success" }, + { id: "gz", text: "广州", color: "blue" }, + { id: "bj", text: "北京" }, + { id: "wh", text: "武汉" }, + { id: "sh", text: "上海" } + ] + }) + }, + radio: { + title: "单选", + search: { show: false }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-custom/custom-layout.vue b/packages/ui/certd-client/src/views/crud/basis/layout-custom/custom-layout.vue new file mode 100644 index 000000000..0f65a9b21 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-custom/custom-layout.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-custom/index.vue b/packages/ui/certd-client/src/views/crud/basis/layout-custom/index.vue new file mode 100644 index 000000000..747c9dd9e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-custom/index.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/layout-custom/mock.js b/packages/ui/certd-client/src/views/crud/basis/layout-custom/mock.js new file mode 100644 index 000000000..d877b043e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/layout-custom/mock.js @@ -0,0 +1,25 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "BasisLayoutCustom", + idGenerator: 0 +}; +const list = [ + { + radio: "1", + name: "张三", + city: "sz" + }, + { + radio: "2", + name: "李四", + city: "gz" + }, + { + radio: "0", + name: "王五", + city: "sh" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/basis/value-change/api.js b/packages/ui/certd-client/src/views/crud/basis/value-change/api.js new file mode 100644 index 000000000..35fdfa0c1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/value-change/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/BasisValueChange"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/basis/value-change/crud.jsx b/packages/ui/certd-client/src/views/crud/basis/value-change/crud.jsx new file mode 100644 index 000000000..15dde4a4b --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/value-change/crud.jsx @@ -0,0 +1,95 @@ +import * as api from "./api"; +import { message } from "ant-design-vue"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + output: {}, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + switch: { + title: "开关", + type: "dict-switch", + dict: dict({ + data: [ + { value: true, label: "开启" }, + { value: false, label: "关闭" } + ] + }), + column: { + component: { + name: "fs-dict-switch", + vModel: "checked" + }, + valueChange(context) { + console.log("column value changed:", context); + } + }, + form: { + valueChange({ value, key, form }) { + console.log("valueChanged,", key, value, form); + message.info(`valueChanged:${key}=${value}`); + } + } + }, + normal: { + title: "value-change", + type: "text", + form: { + valueChange({ value, key, form }) { + console.log("valueChanged,", key, value, form); + message.info(`valueChanged:${key}=${value}`); + } + } + }, + immediate: { + title: "immediate", + type: "text", + search: { + show: true + }, + form: { + valueChange: { + handle({ value, key, form, immediate }) { + console.log("valueChange,", key, value, "isImmediate=", immediate); + message.info(`valueChanged:${key}=${value},isImmediate=${immediate}`); + }, + immediate: true + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/basis/value-change/index.vue b/packages/ui/certd-client/src/views/crud/basis/value-change/index.vue new file mode 100644 index 000000000..0a6bc659c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/value-change/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/basis/value-change/mock.js b/packages/ui/certd-client/src/views/crud/basis/value-change/mock.js new file mode 100644 index 000000000..2d16ebcc6 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/basis/value-change/mock.js @@ -0,0 +1,32 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "BasisValueChange", + idGenerator: 0 +}; +const list = [ + { + ref: "根据showRef显示", + compute: true, + status: "1", + remote: "2", + shower: "---> 点右边编辑查看示例效果", + remote2: "2", + editable: true + }, + { + compute: false, + status: "2", + remote: "0", + remote2: "2", + editable: false + }, + { + compute: true, + status: "0", + remote2: "2", + editable: true + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/button/api.js b/packages/ui/certd-client/src/views/crud/component/button/api.js new file mode 100644 index 000000000..99ab67a8a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/button/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentButton"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/button/crud.jsx b/packages/ui/certd-client/src/views/crud/component/button/crud.jsx new file mode 100644 index 000000000..4e10ee199 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/button/crud.jsx @@ -0,0 +1,115 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict, compute } from "@fast-crud/fast-crud"; +import { message } from "ant-design-vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + //配置表单label的宽度 + labelCol: { span: 6 } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + button: { + title: "按钮", + search: { show: true }, + type: "button", + column: { + component: { + show: compute(({ value }) => { + //当value为null时,不显示 + return value != null; + }), + on: { + // 注意:必须要on前缀 + onClick({ row }) { + message.success("按钮点击:" + row.button); + } + } + } + } + }, + url: { + title: "url", + search: { show: true }, + type: "text", + column: { + show: false + } + }, + link: { + title: "链接", + search: { show: true }, + type: "link", + column: { + component: { + on: { + // 注意:必须要on前缀 + onClick({ row }) { + if (row.url) { + window.open(row.url); + } + } + } + } + }, + form: { + title: "按钮文字" + } + }, + link2: { + title: "手写link配置", + search: { show: true }, + type: "text", //form组件用input + column: { + component: { + name: "fs-button", //列展示组件为button + vModel: "text", // 将row.link2的值赋值给text属性 + type: "link", // 按钮展示为链接样式 + on: { + //注册点击事件 + // 注意:必须要on前缀 + onClick({ row }) { + if (row.url) { + window.open(row.url); + } + } + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/button/index.vue b/packages/ui/certd-client/src/views/crud/component/button/index.vue new file mode 100644 index 000000000..bf86cd95a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/button/index.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/crud/component/button/mock.js b/packages/ui/certd-client/src/views/crud/component/button/mock.js new file mode 100644 index 000000000..bddcce225 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/button/mock.js @@ -0,0 +1,23 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentButton", + idGenerator: 0 +}; +const list = [ + { + button: "张三", + link: "百度", + url: "https://www.baidu.com", + link2: "手写配置" + }, + { + button: "李四", + link: "百度", + url: "https://www.baidu.com", + link2: "手写配置" + }, + {} +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/cascader/api.js b/packages/ui/certd-client/src/views/crud/component/cascader/api.js new file mode 100644 index 000000000..8e851ae50 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/cascader/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentCascader"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/cascader/crud.jsx b/packages/ui/certd-client/src/views/crud/component/cascader/crud.jsx new file mode 100644 index 000000000..2b5206b7d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/cascader/crud.jsx @@ -0,0 +1,137 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ crudRef }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + // 单列布局 + col: { span: 24 }, + labelCol: { span: 4 }, + wrapperCol: { span: 18 } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + cascader: { + title: "级联", + search: { show: true }, + type: "dict-cascader", + dict: dict({ + cloneable: false, + isTree: true, + url: "/mock/dicts/cascaderData?single" + }) + }, + lazyLoad: { + title: "懒加载", + type: "dict-cascader", + dict: dict({ + url: "/mock/tree/GetTreeChildrenByParentId?lazyLoad", + value: "code", + label: "name", + isTree: true, + prototype: true, + getNodesByValues(values) { + //给cell展示组件调用,根据value值获取节点,每行都会请求一次 + if (values == null) { + return []; + } + return requestForMock({ + url: "/mock/tree/GetNodesByValues", + params: { values } + }); + } + }), + form: { + component: { + vModel: "value", + options: [ + { + code: "11", + name: "北京", + isLeaf: false + }, + { + code: "12", + name: "天津", + isLeaf: false + } + ], + loadData: async (selectedOptions) => { + console.log("lazyLoad", selectedOptions); + const targetOption = selectedOptions[selectedOptions.length - 1]; + targetOption.loading = true; + + const ret = await requestForMock({ + url: "/mock/tree/GetTreeChildrenByParentId", + params: { parentId: targetOption.code } + }); + targetOption.loading = false; + const list = []; + for (const item of ret) { + list.push({ + code: item.code, + name: item.name, + isLeaf: item.leaf === true + }); + } + targetOption.children = list; + //options.value = [...options.value]; + }, + changeOnSelect: true + } + } + }, + multiple: { + title: "可搜索,可只选父节点", + type: "dict-cascader", + dict: dict({ + isTree: true, + url: "/mock/dicts/cascaderData?multiple" + }), + form: { + component: { + showSearch: { + filter: (inputValue, path) => { + return path.some((option) => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1); + } + }, + "change-on-select": true + }, + helper: "antd cascader 不支持级联多选" + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/cascader/index.vue b/packages/ui/certd-client/src/views/crud/component/cascader/index.vue new file mode 100644 index 000000000..58428ac78 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/cascader/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/cascader/mock.js b/packages/ui/certd-client/src/views/crud/component/cascader/mock.js new file mode 100644 index 000000000..01bc0582f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/cascader/mock.js @@ -0,0 +1,23 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentCascader", + idGenerator: 0 +}; +const list = [ + { + cascader: ["zhinan", "shejiyuanze", "yizhi"], + lazyLoad: ["11", "1101", "110101", "110101001"], + multiple: ["antdv cascader不支持多选"] + }, + { + cascader: ["zhinan", "shejiyuanze", "yizhi"], + multiple: ["antdv cascader不支持多选"] + }, + { + cascader: ["zhinan", "shejiyuanze", "yizhi"], + multiple: ["antdv cascader不支持多选"] + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/checkbox/api.js b/packages/ui/certd-client/src/views/crud/component/checkbox/api.js new file mode 100644 index 000000000..f1d4edba9 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/checkbox/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentCheckbox"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/checkbox/crud.jsx b/packages/ui/certd-client/src/views/crud/component/checkbox/crud.jsx new file mode 100644 index 000000000..ba55af1bc --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/checkbox/crud.jsx @@ -0,0 +1,50 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ crudRef }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + checkbox: { + title: "状态", + search: { show: true }, + type: "dict-checkbox", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/checkbox/index.vue b/packages/ui/certd-client/src/views/crud/component/checkbox/index.vue new file mode 100644 index 000000000..a1dd4729c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/checkbox/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/checkbox/mock.js b/packages/ui/certd-client/src/views/crud/component/checkbox/mock.js new file mode 100644 index 000000000..1763e9bb6 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/checkbox/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentCheckbox", + idGenerator: 0 +}; +const list = [ + { + checkbox: ["1", "2"] + }, + { + checkbox: "2" + }, + { + checkbox: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/date/api.js b/packages/ui/certd-client/src/views/crud/component/date/api.js new file mode 100644 index 000000000..f6a0197f3 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/date/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentDate"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/date/crud.jsx b/packages/ui/certd-client/src/views/crud/component/date/crud.jsx new file mode 100644 index 000000000..ba405c562 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/date/crud.jsx @@ -0,0 +1,174 @@ +import * as api from "./api"; +import { utils } from "@fast-crud/fast-crud"; +import dayjs from "dayjs"; + +console.log("utils", utils); +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + scroll: { x: 2000 } + }, + rowHandle: { fixed: "right" }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + timestamp: { + title: "时间戳", + type: "datetime", + search: { + show: true, + width: 185, + component: {} + }, + valueBuilder({ value, row, key }) { + console.log("value builder:", key, value, row); + if (value != null) { + row[key] = dayjs(value); + } + }, + valueResolve({ value, row, key }) { + if (value != null) { + row[key] = value.unix(); + } + } + }, + humanize: { + type: ["datetime", "time-humanize"], + title: "人性化时间", + column: { + component: { + options: { + largest: 2 + } + } + } + }, + datetime: { + title: "日期时间", + type: "datetime", + form: { + component: { + valueFormat: "YYYY-MM-DD HH:mm:ss" //输入值的格式 + } + } + }, + format: { + title: "格式化", + type: "datetime", + form: { + component: { + format: "YYYY年MM月DD日 HH:mm", + valueFormat: "YYYY-MM-DD HH:mm:ss" //输入值的格式 + } + }, + column: { + width: 180, + component: { + // 行展示组件使用的dayjs, + format: "YYYY年MM月DD日 HH:mm" + } + } + }, + date: { + title: "仅日期", + type: "date", + form: { + component: { + valueFormat: "YYYY-MM-DD HH:mm:ss", //输入值的格式 + events: { + onChange(context) { + console.log("change", context); + } + } + } + } + }, + time: { + title: "仅时间", + type: "time", + form: { + component: { + valueFormat: "YYYY-MM-DD HH:mm:ss" //输入值的格式 + } + } + }, + disabledDate: { + title: "禁用日期", + type: "date", + form: { + component: { + valueFormat: "YYYY-MM-DD HH:mm:ss", //输入值的格式 + disabledDate(current) { + return current && current < dayjs().endOf("day"); + } + } + } + }, + daterange: { + title: "日期范围", + type: "daterange", + search: { show: true, width: 300 }, + valueBuilder({ row, key }) { + if (!utils.strings.hasEmpty(row.daterangeStart, row.daterangeEnd)) { + row[key] = [dayjs(row.daterangeStart), dayjs(row.daterangeEnd)]; + } + } + }, + datetimerange: { + title: "日期时间范围", + type: "datetimerange", + search: { show: true, width: 300 }, + valueBuilder({ row, key }) { + if (!utils.strings.hasEmpty(row.datetimerangeStart, row.datetimerangeEnd)) { + row[key] = [dayjs(row.datetimerangeStart), dayjs(row.datetimerangeEnd)]; + } + }, + valueResolve({ form, key }) { + const row = form; + if (row[key] != null && !utils.strings.hasEmpty(row[key])) { + row.datetimerangeStart = row[key][0]; + row.datetimerangeEnd = row[key][1]; + } else { + row.datetimerangeStart = null; + row.datetimerangeEnd = null; + } + } + }, + customType: { + title: "自定义字段类型", + type: "time2" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/date/index.vue b/packages/ui/certd-client/src/views/crud/component/date/index.vue new file mode 100644 index 000000000..8ec795284 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/date/index.vue @@ -0,0 +1,47 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/date/mock.js b/packages/ui/certd-client/src/views/crud/component/date/mock.js new file mode 100644 index 000000000..895fddda9 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/date/mock.js @@ -0,0 +1,44 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "ComponentDate", + idGenerator: 0 +}; +const list = [ + { + timestamp: 123123123123, + humanize: new Date().getTime() - 11111111, + datetime: "2019-09-01 11:11:11", + date: "2019-09-02 11:11:11", + format: "2019-09-21 11:11:11", + time: "2019-09-22 12:11:11", + daterangeStart: "2019-09-23 11:11:11", + daterangeEnd: "2019-09-24 11:11:11", + datetimerangeStart: "2019-09-25 11:11:11", + datetimerangeEnd: "2019-09-26 11:11:11" + }, + { + // timestamp: 444444555, + datetime: "2017-09-20 11:11:11", + date: "2019-09-20 11:11:11", + humanize: new Date().getTime() - 22222222, + // time: 12313123334, + daterangeStart: "2019-09-20 11:11:11", + daterangeEnd: "2019-09-21 11:11:11", + datetimerangeStart: "2019-09-20 11:11:11", + datetimerangeEnd: "2019-09-21 11:11:11" + }, + { + // timestamp: 5555555555, + datetime: "2017-09-20 11:11:11", + date: "2019-09-20 11:11:11", + // time: 12313123334, + daterangeStart: "2019-09-20 11:11:11", + daterangeEnd: "2019-09-21 11:11:11", + datetimerangeStart: "2019-09-20 11:11:11", + datetimerangeEnd: "2019-09-21 11:11:11" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/editor/api.js b/packages/ui/certd-client/src/views/crud/component/editor/api.js new file mode 100644 index 000000000..523cfb370 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/editor/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentEditor"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/editor/crud.jsx b/packages/ui/certd-client/src/views/crud/component/editor/crud.jsx new file mode 100644 index 000000000..d32431b78 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/editor/crud.jsx @@ -0,0 +1,107 @@ +import * as api from "./api"; +import { utils, dict, compute } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + title: { + title: "标题", + type: "text", + column: { + width: 400 + }, + form:{ + col: { span: 24 }, + }, + }, + text: { + title: "摘要", + type: "textarea", + form:{ + col: { span: 24 }, + }, + viewForm: { + component: { + name: null, + render(h, scope) { + return
{scope.value}
; + } + } + } + }, + disabled: { + title: "禁用启用", + search: { show: false }, + type: "dict-switch", + dict: dict({ + data: [ + { value: true, label: "禁用" }, + { value: false, label: "启用" } + ] + }) + }, + content_wang: { + title: "内容", + column: { + width: 300, + show: false + }, + type: ["editor-wang5"], // 富文本图片上传依赖file-uploader,请先配置好file-uploader + form: { + helper:"示例已升级到wangEditor5版本,原来的editor-wang目前仍然可以使用,后续fs升级可能会将其删除,请尽快升级到editor-wang5版本", + col: { span: 24 }, + // 动态显隐字段 + // show: compute(({ form }) => { + // return form.change === "wang"; + // }), + rules: [{ required: true, message: "此项必填" }], + component: { + disabled: compute(({ form }) => { + return form.disabled; + }), + id: "1", // 当同一个页面有多个editor时,需要配置不同的id + config: {}, + uploader: { + type: "form", + buildUrl(res) { + return res.url; + } + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/editor/index.vue b/packages/ui/certd-client/src/views/crud/component/editor/index.vue new file mode 100644 index 000000000..61e5e2033 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/editor/index.vue @@ -0,0 +1,44 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/editor/mock.js b/packages/ui/certd-client/src/views/crud/component/editor/mock.js new file mode 100644 index 000000000..8351e0756 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/editor/mock.js @@ -0,0 +1,29 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "ComponentEditor", + idGenerator: 0 +}; +const list = [ + { + title: "d2-crud-plus好用吗?", + text: "非常好用", + content_quill: '

非常好用哦

', + change: "quill" + }, + { + title: "d2-crud-plus有什么优势?", + text: "简单,方便", + content_quill: "简单方便", + change: "wang" + }, + { + title: "1111111", + text: "22222", + content_quill: "3333", + change: "wang" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/icon/api.js b/packages/ui/certd-client/src/views/crud/component/icon/api.js new file mode 100644 index 000000000..4db9ed9ba --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/icon/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentIcon"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/icon/crud.jsx b/packages/ui/certd-client/src/views/crud/component/icon/crud.jsx new file mode 100644 index 000000000..ad9c38bed --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/icon/crud.jsx @@ -0,0 +1,75 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + icon: { + title: "icon", + search: { show: true }, + type: "text", + column: { + component: { + name: "fs-icon", + vModel: "icon", + style: "font-size:18px" + } + }, + form: { + helper: { + render() { + return ( + + 点击此处选择图标名称 + + ); + } + } + } + }, + svg: { + title: "svg", + search: { show: true }, + type: "text", + column: { + component: { + name: "fs-icon", + vModel: "icon", + style: "font-size:18px" + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/icon/index.vue b/packages/ui/certd-client/src/views/crud/component/icon/index.vue new file mode 100644 index 000000000..f8f1e9293 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/icon/index.vue @@ -0,0 +1,42 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/icon/mock.js b/packages/ui/certd-client/src/views/crud/component/icon/mock.js new file mode 100644 index 000000000..fe2de8ed2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/icon/mock.js @@ -0,0 +1,22 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentIcon", + idGenerator: 0 +}; +const list = [ + { + icon: "ri:24-hours-fill", + svg:"svg:icon-compass" + }, + { + icon: "ion:add-circle-outline", + svg:"svg:icon-left-circle" + }, + { + icon: "ion:american-football-sharp", + svg:"svg:icon-Dollar" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/json/api.js b/packages/ui/certd-client/src/views/crud/component/json/api.js new file mode 100644 index 000000000..a62736e62 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/json/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentJson"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/json/crud.jsx b/packages/ui/certd-client/src/views/crud/component/json/crud.jsx new file mode 100644 index 000000000..898fb5d30 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/json/crud.jsx @@ -0,0 +1,78 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + wrapper: { + async onOpened({ mode, formRef }) { + if (!formRef.form.async) { + setTimeout(() => { + formRef.form.async = { aaa: "11", bb: "111" }; + }, 2000); + } + } + } + }, + columns: { + json: { + title: "json", + type: "json", + form: { + valueBuilder({ form }) { + if (form.json == null) { + return; + } + form.json = JSON.parse(form.json); + }, + valueResolve({ form }) { + if (form.json == null) { + return; + } + form.json = JSON.stringify(form.json); + } + } + }, + async: { + title: "异步加载", + type: "json", + form: { + // 上面form.wrapper.onOpened里面配置了异步加载 + helper: "在onOpened里面配置异步加载json字符串", + valueBuilder({ form }) { + if (form.async == null) { + return; + } + form.async = JSON.parse(form.async); + }, + valueResolve({ form }) { + if (form.async == null) { + return; + } + form.async = JSON.stringify(form.async); + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/json/index.vue b/packages/ui/certd-client/src/views/crud/component/json/index.vue new file mode 100644 index 000000000..34868881f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/json/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/json/mock.js b/packages/ui/certd-client/src/views/crud/component/json/mock.js new file mode 100644 index 000000000..727a17889 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/json/mock.js @@ -0,0 +1,18 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentJson", + idGenerator: 0 +}; +const list = [ + { + json: '{"a":1,"b":2}', + async: null + }, + { + json: '{"a":3,"b":4}', + async: null + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/number/api.js b/packages/ui/certd-client/src/views/crud/component/number/api.js new file mode 100644 index 000000000..30c88fc3f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/number/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentNumber"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/number/crud.jsx b/packages/ui/certd-client/src/views/crud/component/number/crud.jsx new file mode 100644 index 000000000..057f7b18a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/number/crud.jsx @@ -0,0 +1,71 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + integer: { + title: "整数", + search: { show: true }, + type: "number" + }, + float: { + title: "小数", + type: "number", + form: { + component: { + step: "0.1" + } + } + }, + format: { + title: "格式化", + type: "number", + form: { + component: { + formatter: (value) => `${value}%`, + parser: (value) => value.replace("%", "") + } + }, + column: { + formatter({ value }) { + return value + "%"; + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/number/index.vue b/packages/ui/certd-client/src/views/crud/component/number/index.vue new file mode 100644 index 000000000..f6989e027 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/number/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/number/mock.js b/packages/ui/certd-client/src/views/crud/component/number/mock.js new file mode 100644 index 000000000..6b8854a1c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/number/mock.js @@ -0,0 +1,25 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentNumber", + idGenerator: 0 +}; +const list = [ + { + integer: 1, + float: 1.1, + format: 100 + }, + { + integer: 2, + float: 1.2, + format: 100 + }, + { + integer: 3, + float: 1.3, + format: 100 + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/radio/api.js b/packages/ui/certd-client/src/views/crud/component/radio/api.js new file mode 100644 index 000000000..761dae47f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/radio/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentRadio"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/radio/crud.jsx b/packages/ui/certd-client/src/views/crud/component/radio/crud.jsx new file mode 100644 index 000000000..cf7320196 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/radio/crud.jsx @@ -0,0 +1,73 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + }, + button: { + title: "按钮样式", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }), + form: { + component: { + optionName: "a-radio-button" + } + } + }, + bool: { + title: "布尔类型", + search: { show: true }, + type: "dict-radio", + dict: dict({ + data: [ + { value: true, label: "TRUE" }, + { value: false, label: "FALSE" } + ] + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/radio/index.vue b/packages/ui/certd-client/src/views/crud/component/radio/index.vue new file mode 100644 index 000000000..d851e5852 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/radio/index.vue @@ -0,0 +1,42 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/radio/mock.js b/packages/ui/certd-client/src/views/crud/component/radio/mock.js new file mode 100644 index 000000000..647514850 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/radio/mock.js @@ -0,0 +1,24 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentRadio", + idGenerator: 0 +}; +const list = [ + { + radio: "1", + button: "1", + bool: true + }, + { + radio: "2", + button: "2", + bool: false + }, + { + radio: "0", + button: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/select/api.js b/packages/ui/certd-client/src/views/crud/component/select/api.js new file mode 100644 index 000000000..6c9e2511d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/select/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentSelect"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/select/crud.jsx b/packages/ui/certd-client/src/views/crud/component/select/crud.jsx new file mode 100644 index 000000000..a04fafa05 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/select/crud.jsx @@ -0,0 +1,311 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +import _ from "lodash-es"; +function useSearchRemote() { + let lastFetchId = 0; + + const state = { + data: ref([]), + fetching: ref(false) + }; + const fetchUser = _.debounce((value) => { + console.log("fetching user", value); + lastFetchId += 1; + + const fetchId = lastFetchId; + + state.data.value = []; + + state.fetching.value = true; + + fetch("https://randomuser.me/api/?results=5") + .then((response) => response.json()) + .then((body) => { + if (fetchId !== lastFetchId) { + // for fetch callback order + return; + } + const data = body.results.map((user) => ({ + text: `${user.name.first} ${user.name.last}`, + value: user.login.username + })); + state.data.value = data; + state.fetching.value = false; + }); + }, 800); + + return { + fetchUser, + searchState: state + }; +} +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + const dictRef = dict({ + value: "id", + label: "text", + data: [ + { id: "sz", text: "深圳", color: "success" }, + { id: "gz", text: "广州", color: "blue" }, + { id: "bj", text: "北京" }, + { id: "wh", text: "武汉" }, + { id: "sh", text: "上海" } + ] + }); + + function dynamicUpdateDictOptions() { + dictRef.data.push({ id: "xg", text: "香港" }); + //dictRef.toMap(); + } + + const { fetchUser, searchState } = useSearchRemote(); + return { + dynamicUpdateDictOptions, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + // 单列布局 + col: { span: 24 }, + labelCol: { span: 4 }, + wrapperCol: { span: 18 } + }, + rowHandle: { + fixed: "right", + align: "center" + }, + table: { + scroll: { + //启用横向滚动条,设置一个大于所有列宽之和的值,一般大于表格宽度 + x: 1400 + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + statusLocal: { + title: "单选本地", + type: "dict-select", + dict: dictRef + }, + statusRemote: { + title: "单选远程", + search: { + show: true, + rules: null, + component: { + style: { width: "100px" } + } + }, + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?simple" + }), + form: { + rules: [{ required: true, message: "请选择一个选项" }] + } + }, + filter: { + title: "本地过滤", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?simple" + }), + form: { + component: { + showSearch: true, + //默认的filterOption仅支持value的过滤,label并不会加入查询 + //所以需要自定义filterOption + filterOption(inputValue, option) { + return option.label.indexOf(inputValue) >= 0 || option.value.indexOf(inputValue) >= 0; + } + } + } + }, + search: { + title: "远程搜索", + search: { show: true, component: { style: { width: "240px" } } }, + form: { + component: { + name: "a-select", + vModel: "value", + filterOption: false, + //labelInValue: true, + mode: "multiple", + showSearch: true, + allowClear: true, + placeholder: "输入远程搜索,数据仅供演示", + options: searchState.data, + onSearch(value) { + fetchUser(value); + }, + children: { + notFoundContent() { + if (searchState.fetching.value) { + return ; + } + return "暂无记录"; + } + } + } + } + }, + customDictGetData: { + title: "自定义字典请求", + search: { show: false }, + type: "dict-select", + dict: dict({ + getData({ dict }) { + // 覆盖全局获取字典请求配置 + console.log(`我是从自定义的getData方法中加载的数据字典`, dict); + return requestForMock({ + url: "/mock/dicts/OpenStatusEnum?cache", + method: "get" + }); + } + }), + form: { + value: "2", //默认值, 注意search也会影响到,需要将search.value=null,取消search的默认值 + helper: "dict.getData可以覆盖全局配置的getRemoteDictFunc" + }, + column: { + width: 120, + component: { + type: "text" // 不使用tag,纯文本展示 + } + } + }, + disabledOptions: { + title: "禁用某个选项", + key: "disabledOptions", + type: "dict-select", + dict: dict({ + cloneable: true, + url: "/mock/dicts/OpenStatusEnum?disabledOptions" + }), + form: { + component: { + dict: { + // 此处dict配置会覆盖上面dict的属性 + prototype: true, // form表单的dict设置为原型复制,每次初始化时都会重新loadDict + onReady({ dict }) { + console.log("字典请求ready", dict); + dict.data[0].disabled = true; // 禁用某个选项, 还可以自己修改选项 + } + } + }, + helper: "禁用字典选项" + }, + column: { + width: 150 + } + }, + firstDefault: { + title: "默认值", + type: "dict-select", + dict: dict({ + cloneable: true, + url: "/mock/dicts/OpenStatusEnum?disabledOptions" + }), + form: { + component: { + //监听 dict-change事件 + onDictChange({ dict, form, key }) { + console.log("dict data changed", dict, key); + if (dict.data != null && form.firstDefault == null) { + form.firstDefault = dict.data[0].value; + } + } + // 下面的方法也可以,注意要配置dict.prototype:true + // dict: { + // // 此处dict配置会覆盖上面dict的属性 + // // form表单的dict设置为原型复制,每次初始化时都会重新loadDict + // prototype: true, + // + // onReady({ dict, form }) { + // console.log("字典请求ready", dict, form, getComponentRef); + // // prototype= true 才能获取到form表单数据 + // form.firstDefault = dict.data[0].value; + // } + // } + }, + helper: "默认选择第一个选项" + } + }, + multiple: { + title: "多选自动染色", + sortable: true, + type: "dict-select", + form: { + title: "多选本地", + component: { + mode: "multiple" + } + }, + dict: dict({ + data: [ + { value: "sz", label: "深圳", color: "success" }, + { value: "gz", label: "广州" }, + { value: "wh", label: "武汉" }, + { value: "sh", label: "上海" }, + { value: "hz", label: "杭州" }, + { value: "bj", label: "北京", color: "red" } + ] + }), + column: { + width: 290, + component: { + color: "auto", // 自动染色 + defaultLabel: "未知城市" //无数据字典时的默认文本 + } + } + }, + statusSimple: { + title: "普通选择", + form: { + component: { + name: "a-select", + vModel: "value", + options: [ + { value: "sz", label: "深圳", color: "success" }, + { value: "gz", label: "广州", color: "blue" }, + { value: "bj", label: "北京" }, + { value: "wh", label: "武汉" }, + { value: "sh", label: "上海" } + ] + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/select/index.vue b/packages/ui/certd-client/src/views/crud/component/select/index.vue new file mode 100644 index 000000000..8278b4564 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/select/index.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/select/mock.js b/packages/ui/certd-client/src/views/crud/component/select/mock.js new file mode 100644 index 000000000..d74488775 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/select/mock.js @@ -0,0 +1,42 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentSelect", + idGenerator: 0 +}; +const list = [ + { + statusLocal: "sz", + customDictUrl: "0", + statusValue: 1, + multiple: ["sz", "bj", "gz", "sh", "hz", "xz", "xg"], + checkbox: "0", + select_local: "sz", + statusRemote: "0", + status_custom_2: "0", + customDictGetData: "1", + checkbox_btn: "1" + }, + { + statusLocal: "xg", + customDictUrl: "1", + statusValue: 2, + statusRemote: "1", + status_custom_2: "2", + select_local: "gz", + multiple: ["sh", "sz"], + checkbox: "0" + }, + { + statusLocal: "gz", + customDictUrl: "1", + statusValue: 1, + disabledCache: "1", + disabledOptions: "2", + select_local: "gz", + multiple: ["sh", "gz"], + checkbox: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/switch/api.js b/packages/ui/certd-client/src/views/crud/component/switch/api.js new file mode 100644 index 000000000..f2e25bed5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/switch/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentSwitch"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/switch/crud.jsx b/packages/ui/certd-client/src/views/crud/component/switch/crud.jsx new file mode 100644 index 000000000..e2d751a75 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/switch/crud.jsx @@ -0,0 +1,141 @@ +import * as api from "./api"; +import { dict, compute } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + switch: { + title: "状态", + search: { show: true }, + type: "dict-switch", + dict: dict({ + data: [ + { value: false, label: "开启" }, + { value: true, label: "关闭" } + ] + }) + }, + notBool: { + title: "自定义value", + search: { show: true }, + type: "dict-switch", + dict: dict({ + data: [ + { value: "1", label: "开启" }, + { value: "2", label: "关闭" } + ] + }) + }, + switchLabel: { + title: "切换字段label", + search: { show: true }, + type: "dict-switch", + dict: dict({ + data: [ + { value: true, label: "开启" }, + { value: false, label: "关闭" } + ] + }), + column: { + show: false + } + }, + labelTarget: { + title: "我将被切换", + type: "text", + column: { + show: false + }, + form: { + label: compute(({ form }) => { + return form.switchLabel ? "我将被切换" : "再切换一下"; + }) + } + }, + cellSwitch: { + title: "cell显示", + search: { show: true }, + type: "dict-switch", + form: { + component: {} + }, + column: { + component: { + name: "fs-dict-switch", + vModel: "checked", + onChange: (value) => { + console.log("onChange", value); + } + // onChange: compute((context) => { + // //动态onChange方法测试 + // return () => { + // console.log("onChange", context.row.cellSwitch); + // }; + // }) + } + }, + dict: dict({ + data: [ + { value: true, label: "开启" }, + { value: false, label: "关闭" } + ] + }) + }, + showTarget: { + title: "显隐目标", + type: "text", + column: { + component: { + name: "fs-values-format", + show: compute((context) => { + //根据cellSwitch字段显隐 + return context.row.cellSwitch === true; + }) + } + }, + search: { + show: false + }, + form: { + show: compute((context) => { + console.log("context", context); + //根据cellSwitch字段显隐 + return context.form.cellSwitch === true; + }) + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/switch/index.vue b/packages/ui/certd-client/src/views/crud/component/switch/index.vue new file mode 100644 index 000000000..beb30f7df --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/switch/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/switch/mock.js b/packages/ui/certd-client/src/views/crud/component/switch/mock.js new file mode 100644 index 000000000..42772b1f7 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/switch/mock.js @@ -0,0 +1,27 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentSwitch", + idGenerator: 0 +}; +const list = [ + { + switch: true, + cellSwitch: true, + notBool: "1", + showTarget: "点左边开关显示或隐藏" + }, + { + switch: false, + cellSwitch: true, + notBool: "2", + showTarget: "点左边开关显示或隐藏" + }, + { + switch: true, + cellSwitch: false, + showTarget: "点左边开关显示或隐藏" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/text/api.js b/packages/ui/certd-client/src/views/crud/component/text/api.js new file mode 100644 index 000000000..2111f7724 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/text/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentText"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/text/crud.jsx b/packages/ui/certd-client/src/views/crud/component/text/crud.jsx new file mode 100644 index 000000000..24850e9e5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/text/crud.jsx @@ -0,0 +1,130 @@ +import * as api from "./api"; +import { compute } from "@fast-crud/fast-crud"; +import { resolveDirective, withDirectives } from "vue"; + +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + name: { + title: "姓名", + type: "text", //虽然不写也能正确显示组件,但不建议省略它 + search: { show: true }, + form: { + component: { + maxlength: 20 + } + } + }, + search: { + title: "搜索", + type: "text", + form: { + component: { + addonAfter: "后置", + suffix: "suffix", + children: { + addonBefore() { + return ; + } + } + } + } + }, + password: { + title: "密码", + type: "password", + column: { + //一般密码不显示在列里面 + show: false + } + }, + // copy: { + // title: "剪贴板", + // type: "text", + // column: { + // cellRender({ value, row }) { + // const content = ( + //
+ // {value} + // 复制 + //
+ // ); + // const clipboard = resolveDirective("clipboard"); + // return withDirectives(content, [[clipboard, value]]); + // } + // } + // }, + copy: { + title: "剪贴板", + type: ["text", "copyable"] + }, + intro: { + title: "简介", + type: "textarea", + form: { + component: { showWordLimit: true, maxlength: 200 } + }, + column: { + ellipsis: true, + showTitle: true + } + }, + render: { + title: "复杂输入(render)", + form: { + title: "复杂输入", + component: { + render(context) { + console.log("context scope", context); + return ( + + + + + ); + } + } + } + }, + render2: { + title: "我的值是由复杂输入列输入的", + column: { + width: "300px" + }, + form: { + show: false + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/text/index.vue b/packages/ui/certd-client/src/views/crud/component/text/index.vue new file mode 100644 index 000000000..a97ef5519 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/text/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/text/mock.js b/packages/ui/certd-client/src/views/crud/component/text/mock.js new file mode 100644 index 000000000..4bc9df77c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/text/mock.js @@ -0,0 +1,43 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentText", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + date: "2016-05-02", + status: "0", + province: "1", + avatar: "https://alicdn.antdv.com/vue.png", + show: true, + city: "sz", + address: "123123", + zip: "518000", + intro: "王小虎是element-plus的table示例出现的名字", + copy: "测试文本" + }, + { + name: "张三", + date: "2016-05-04", + status: "1", + province: "2", + copy: "测试文本" + }, + { + name: "李四", + date: 2232433534511, + status: "1", + province: "0", + copy: "测试文本" + }, + { + name: "王五", + date: "2016-05-03", + status: "2", + province: "wh,gz" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/tree/api.js b/packages/ui/certd-client/src/views/crud/component/tree/api.js new file mode 100644 index 000000000..b9db03f12 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/tree/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentTree"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/tree/crud.jsx b/packages/ui/certd-client/src/views/crud/component/tree/crud.jsx new file mode 100644 index 000000000..1d3ce5abf --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/tree/crud.jsx @@ -0,0 +1,81 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + tree: { + title: "树形选择", + search: { show: false }, + type: "dict-tree", + dict: dict({ + isTree: true, + url: "/mock/dicts/cascaderData?single" + }) + }, + multiple: { + title: "多选", + search: { show: false }, + type: "dict-tree", + dict: dict({ + cloneable: false, + isTree: true, + url: "/mock/dicts/cascaderData?single" + }), + form: { + component: { + "tree-checkable": true + } + } + }, + fieldReplace: { + title: "修改options的value字段名", + search: { show: false }, + type: "dict-tree", + dict: dict({ + isTree: true, + url: "/mock/dicts/littlePca", + value: "code", + label: "name" + }), + form: { + component: { + fieldNames: { label: "name", key: "code", value: "code" } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/tree/index.vue b/packages/ui/certd-client/src/views/crud/component/tree/index.vue new file mode 100644 index 000000000..b5c740739 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/tree/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/tree/mock.js b/packages/ui/certd-client/src/views/crud/component/tree/mock.js new file mode 100644 index 000000000..ed3a1d394 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/tree/mock.js @@ -0,0 +1,16 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentTree", + idGenerator: 0 +}; +const list = [ + { + tree: "zhinan", + multiple: ["zhinan", "yizhi"] + }, + { tree: "zhinan" }, + { tree: "zhinan" } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/alioss/api.js b/packages/ui/certd-client/src/views/crud/component/uploader/alioss/api.js new file mode 100644 index 000000000..c5871d5bf --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/alioss/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/AliossUploader"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/alioss/crud.jsx b/packages/ui/certd-client/src/views/crud/component/uploader/alioss/crud.jsx new file mode 100644 index 000000000..c5dd0c412 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/alioss/crud.jsx @@ -0,0 +1,75 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + file: { + title: "阿里云上传", + type: "file-uploader", + form: { + component: { + uploader: { + type: "alioss" + } + } + } + }, + pictureCard: { + title: "照片墙", + type: "image-uploader", + form: { + component: { + uploader: { + type: "alioss" + } + } + } + }, + cropper: { + title: "裁剪", + type: "cropper-uploader", + form: { + component: { + uploader: { + type: "alioss" + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/alioss/index.vue b/packages/ui/certd-client/src/views/crud/component/uploader/alioss/index.vue new file mode 100644 index 000000000..0a305710c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/alioss/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/alioss/mock.js b/packages/ui/certd-client/src/views/crud/component/uploader/alioss/mock.js new file mode 100644 index 000000000..c3de48577 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/alioss/mock.js @@ -0,0 +1,24 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "AliossUploader", + idGenerator: 0 +}; +const list = [ + { + avatar_error1: "http://greper.handsfree.work/extends/avatar1.jpg", + avatar_error2: "http://greper.handsfree.work/extends/avatar1.jpg", + avatar: "http://greper.handsfree.work/extends/avatar.jpg", + file: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + image: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + image2: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"] + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/cos/api.js b/packages/ui/certd-client/src/views/crud/component/uploader/cos/api.js new file mode 100644 index 000000000..536456ea9 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/cos/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/CosUploader"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/cos/crud.jsx b/packages/ui/certd-client/src/views/crud/component/uploader/cos/crud.jsx new file mode 100644 index 000000000..05e54e162 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/cos/crud.jsx @@ -0,0 +1,75 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + file: { + title: "腾讯云", + type: "file-uploader", + form: { + component: { + uploader: { + type: "cos" + } + } + } + }, + pictureCard: { + title: "照片墙", + type: "image-uploader", + form: { + component: { + uploader: { + type: "cos" + } + } + } + }, + cropper: { + title: "裁剪", + type: "cropper-uploader", + form: { + component: { + uploader: { + type: "cos" + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/cos/index.vue b/packages/ui/certd-client/src/views/crud/component/uploader/cos/index.vue new file mode 100644 index 000000000..8b4bc1ef5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/cos/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/cos/mock.js b/packages/ui/certd-client/src/views/crud/component/uploader/cos/mock.js new file mode 100644 index 000000000..5c608212f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/cos/mock.js @@ -0,0 +1,22 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "CosUploader", + idGenerator: 0 +}; +const list = [ + { + avatar: "http://greper.handsfree.work/extends/avatar.jpg", + file: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + image: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + image2: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"] + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/cropper/api.js b/packages/ui/certd-client/src/views/crud/component/uploader/cropper/api.js new file mode 100644 index 000000000..5113d49c4 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/cropper/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/CropperUploader"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/cropper/crud.jsx b/packages/ui/certd-client/src/views/crud/component/uploader/cropper/crud.jsx new file mode 100644 index 000000000..4d8e168c9 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/cropper/crud.jsx @@ -0,0 +1,110 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + cropper: { + title: "头像裁剪上传", + type: "cropper-uploader" + }, + avatar: { + title: "数量限制", + type: "cropper-uploader", + form: { + component: { + limit: 5 //默认限制1个,即头像上传,0为不限制 + } + } + }, + aspect: { + title: "按比例裁剪", + type: "cropper-uploader", + form: { + component: { + cropper: { + aspectRatio: 2 + } + } + } + }, + alioss: { + title: "alioss", + type: "cropper-uploader", + form: { + component: { + uploader: { + type: "alioss" + } + } + } + }, + qiniu: { + title: "七牛", + type: "cropper-uploader", + form: { + component: { + uploader: { + type: "qiniu" + } + } + } + }, + cos: { + title: "腾讯cos", + type: "cropper-uploader", + form: { + component: { + uploader: { + type: "cos" + } + } + } + }, + form: { + title: "表单", + type: "cropper-uploader", + form: { + component: { + uploader: { + type: "form" + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/cropper/index.vue b/packages/ui/certd-client/src/views/crud/component/uploader/cropper/index.vue new file mode 100644 index 000000000..3e6a98bf8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/cropper/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/cropper/mock.js b/packages/ui/certd-client/src/views/crud/component/uploader/cropper/mock.js new file mode 100644 index 000000000..83108782a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/cropper/mock.js @@ -0,0 +1,24 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "CropperUploader", + idGenerator: 0 +}; +const list = [ + { + avatar_error1: "http://greper.handsfree.work/extends/avatar1.jpg", + avatar_error2: "http://greper.handsfree.work/extends/avatar1.jpg", + avatar: "http://greper.handsfree.work/extends/avatar.jpg", + file: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + image: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + image2: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"] + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/form/api.js b/packages/ui/certd-client/src/views/crud/component/uploader/form/api.js new file mode 100644 index 000000000..af8d42b4a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/form/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentUploader"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/form/crud.jsx b/packages/ui/certd-client/src/views/crud/component/uploader/form/crud.jsx new file mode 100644 index 000000000..7a9cb0646 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/form/crud.jsx @@ -0,0 +1,223 @@ +import * as api from "./api"; +import { AllUploadSuccessValidator } from "@fast-crud/fast-extends"; +import { dict } from "@fast-crud/fast-crud"; +import { nextTick } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + wrapper: { + async onOpened() { + // 异步组件实例的获取 + const componentRef = await expose.getFormComponentRef("file", true); + console.log("componentRef", componentRef); + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + file: { + title: "表单上传", + type: "file-uploader", + form: { + component: { + multiple: true, //可选择多个 + uploader: { + type: "form" + } + } + }, + column: { + component: { + // 如果你后台返回的值不是一个完整的url,那么展示时就无法显示和点击 + // 需要你本地根据value构建文件的url。 + // 支持异步 + async buildUrl(value) { + return value; + } + } + } + }, + pictureCard: { + title: "照片墙", + type: "image-uploader", + form: { + component: { + limit: 1, + uploader: { + type: "form" + } + }, + helper: "最大可上传1个文件" + }, + column: { + component: { + buildPreviewUrl({ url, index }) { + if (index === 0) { + return "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"; + } else { + return url + "?preview=600x600"; + } + } + } + } + }, + pictureCard2: { + title: "通过urls显示", + type: "image-uploader", + column: { + component: { + urls: [ + { + url: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?1", + previewUrl: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?preview1" + }, + { + url: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?2", + previewUrl: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png?preview2" + } + ] + } + } + }, + avatar: { + title: "头像上传", + type: "avatar-uploader", + form: { + component: { + uploader: { + type: "form" + } + }, + helper: "就是照片墙limit=1的效果" + } + }, + cropper: { + title: "裁剪", + type: "cropper-uploader", + form: { + component: { + uploader: { + type: "form" + } + } + } + }, + keyValueType: { + title: "valueType为key", + type: "file-uploader", + form: { + component: { + uploader: { + type: "form" + }, + valueType: "key", + async buildUrl(value) { + return new Promise((resolve) => { + const url = "http://www.docmirror.cn:7070/api/upload/form/download?key=" + value; + resolve(url); + }); + } + } + }, + column: { + component: { + async buildUrl(value) { + return new Promise((resolve) => { + const url = "http://www.docmirror.cn:7070/api/upload/form/download?key=" + value; + resolve(url); + }); + } + } + } + }, + limit: { + title: "限制数量", + type: "file-uploader", + form: { + component: { + limit: 2, + uploader: { + type: "form" + } + }, + helper: "最大可上传2个文件" + } + }, + sizeLimit: { + title: "限制大小", + type: "file-uploader", + form: { + component: { + sizeLimit: 1024, + uploader: { + type: "form" + } + }, + helper: "大小不能超过1k" + } + }, + accept: { + title: "限制类型", + type: "file-uploader", + form: { + component: { + accept: "*.jpg,*.png" + }, + helper: "只能上传jpg或者png" + } + }, + validation: { + title: "校验", + type: "file-uploader", + form: { + rules: [ + { required: true, message: "此项必传" }, + { + validator: AllUploadSuccessValidator(), //如果要自定义校验规则则需要手动配置这个 + message: "还有文件正在上传,请稍候" + } + ], + helper: "大小不能超过50M,文件未上传完成之前,阻止提交", + component: { + uploader: { + type: "form", + sizeLimit: 1024 * 1024 * 50 + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/form/index.vue b/packages/ui/certd-client/src/views/crud/component/uploader/form/index.vue new file mode 100644 index 000000000..eaae1cac8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/form/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/form/mock.js b/packages/ui/certd-client/src/views/crud/component/uploader/form/mock.js new file mode 100644 index 000000000..e4fda8db3 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/form/mock.js @@ -0,0 +1,24 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentUploader", + idGenerator: 0 +}; +const list = [ + { + avatar: "http://greper.handsfree.work/extends/avatar.jpg", + file: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + pictureCard: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + limit: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + statusRemote: "0", + keyValueType: "/2022-12-20/qygzqdjd1g.yaml" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/api.js b/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/api.js new file mode 100644 index 000000000..2d79f7b54 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/QiniuUploader"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/crud.jsx b/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/crud.jsx new file mode 100644 index 000000000..89c787c3e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/crud.jsx @@ -0,0 +1,73 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + file: { + title: "七牛云上传", + type: "file-uploader", + form: { + component: { + uploader: { + type: "qiniu" + } + } + } + }, + pictureCard: { + title: "照片墙", + type: "image-uploader", + form: { + component: { + uploader: { + type: "qiniu" + } + } + } + }, + cropper: { + title: "裁剪", + type: "cropper-uploader", + form: { + component: { + uploader: { + type: "qiniu" + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/index.vue b/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/index.vue new file mode 100644 index 000000000..0a305710c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/mock.js b/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/mock.js new file mode 100644 index 000000000..45c796850 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/component/uploader/qiniu/mock.js @@ -0,0 +1,22 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "QiniuUploader", + idGenerator: 0 +}; +const list = [ + { + avatar: "http://greper.handsfree.work/extends/avatar.jpg", + file: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + image: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"], + image2: ["http://greper.handsfree.work/extends/avatar.jpg", "https://www.baidu.com/img/bd_logo1.png"] + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/debug/select/api.js b/packages/ui/certd-client/src/views/crud/debug/select/api.js new file mode 100644 index 000000000..06a6bd3e5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/debug/select/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/DebugSelect"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/debug/select/crud.jsx b/packages/ui/certd-client/src/views/crud/debug/select/crud.jsx new file mode 100644 index 000000000..d3bee6123 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/debug/select/crud.jsx @@ -0,0 +1,106 @@ +import * as api from "./api"; +import { requestForMock } from "/src/api/service"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + // 单列布局 + col: { span: 24 }, + labelCol: { span: 4 }, + wrapperCol: { span: 18 } + }, + rowHandle: { + fixed: "right" + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + + statusRemote: { + title: "单选远程", + search: { + show: true, + value: [] + }, + type: "dict-select", + dict: dict({ + url: "/mock/dicts/_OpenStatusEnum2?simple", + value: "id", + label: "text" + }), + form: { + component: { mode: "multiple" }, + rules: [{ required: true, message: "请选择一个选项" }] + }, + column: { + width: 200 + } + }, + id2: { + title: "ID", + key: "id", + type: "number", + column: { + width: 300 + }, + form: { + show: false + } + }, + id3: { + title: "ID", + key: "id", + type: "number", + column: { + width: 300 + }, + form: { + show: false + } + }, + id4: { + title: "ID", + key: "id", + type: "number", + column: { + width: 300 + }, + form: { + show: false + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/debug/select/index.vue b/packages/ui/certd-client/src/views/crud/debug/select/index.vue new file mode 100644 index 000000000..e7105c522 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/debug/select/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/debug/select/mock.js b/packages/ui/certd-client/src/views/crud/debug/select/mock.js new file mode 100644 index 000000000..2cb1e1750 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/debug/select/mock.js @@ -0,0 +1,9 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "DebugSelect", + idGenerator: 0 +}; +const list = []; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/dict/cloneable/api.js b/packages/ui/certd-client/src/views/crud/dict/cloneable/api.js new file mode 100644 index 000000000..a6c63db5f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/cloneable/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/DictCloneable"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/dict/cloneable/crud.jsx b/packages/ui/certd-client/src/views/crud/dict/cloneable/crud.jsx new file mode 100644 index 000000000..4ad17c789 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/cloneable/crud.jsx @@ -0,0 +1,88 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + const remoteDict = dict({ + cloneable: true, + url: "/mock/dicts/OpenStatusEnum" + }); + + return { + crudOptions: { + remoteDict, + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + remote: { + title: "远程字典", + search: { show: true }, + dict: remoteDict, + type: "dict-select", + form: { + component: { dict: { cache: false } } + } + }, + modifyDict: { + title: "动态修改字典", + search: { show: false }, + type: "text", + column: { + component: { + name: "a-switch", + vModel: "checked" + }, + valueChange({ row, getComponentRef }) { + // 这里不能使用remoteDict,因为在分发时已经clone到form配置中了 + // 这里dict修改不会影响列里面的数据 + const targetDict = getComponentRef("remote").dict; + targetDict.url = row.modifyDict ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote"; + targetDict.reloadDict(); + } + }, + form: { + component: { + name: "a-switch", + vModel: "checked" + }, + valueChange({ form, getComponentRef }) { + // 这里不能使用remoteDict,因为在分发时已经clone到form配置中了 + // 这里dict修改不会影响列里面的数据 + const targetDict = getComponentRef("remote").dict; + targetDict.url = form.modifyDict ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote"; + targetDict.reloadDict(); + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/dict/cloneable/index.vue b/packages/ui/certd-client/src/views/crud/dict/cloneable/index.vue new file mode 100644 index 000000000..4850b6200 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/cloneable/index.vue @@ -0,0 +1,48 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/dict/cloneable/mock.js b/packages/ui/certd-client/src/views/crud/dict/cloneable/mock.js new file mode 100644 index 000000000..acb0f31b8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/cloneable/mock.js @@ -0,0 +1,21 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "DictCloneable", + idGenerator: 0 +}; +const list = [ + { + status: "1", + remote: "2" + }, + { + status: "2", + remote: "0" + }, + { + status: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/dict/prototype/api.js b/packages/ui/certd-client/src/views/crud/dict/prototype/api.js new file mode 100644 index 000000000..08ab81955 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/prototype/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/DictPrototype"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/dict/prototype/crud.jsx b/packages/ui/certd-client/src/views/crud/dict/prototype/crud.jsx new file mode 100644 index 000000000..c40a2b070 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/prototype/crud.jsx @@ -0,0 +1,128 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { requestForMock } from "../../../../api/service"; + +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + const remoteDict = dict({ + prototype: true, //这个dict只是一个原型,引用它的dict组件初始化时都会把此dict对象clone一份 + url: "/mock/dicts/OpenStatusEnum" + }); + + const dynamicUrlDict = dict({ + cache: true, + prototype: true, //这个dict只是一个原型,引用它的dict组件初始化时都会把此dict对象clone一份 + url({ row }) { + return row.switch ? "/mock/dicts/moreOpenStatusEnum" : "/mock/dicts/OpenStatusEnum"; + } + }); + const dynamicDict = dict({ + cache: true, + prototype: true, //这个dict只是一个原型,引用它的dict组件初始化时都会把此dict对象clone一份 + url({ row }) { + return row.switch ? "/mock/dicts/moreOpenStatusEnum" : "/mock/dicts/OpenStatusEnum"; + }, + async getData({ url }) { + return await requestForMock({ url }); + } + }); + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + remote: { + title: "远程字典", + search: { show: true }, + dict: remoteDict, + type: "dict-select" + }, + modifyDict: { + title: "动态修改字典", + search: { show: true }, + type: "text", + form: { + helper: "此处可以动态切换左边select的options", + component: { + name: "a-switch", + vModel: "checked" + }, + valueChange({ form, value, getComponentRef }) { + console.log("form", value); + const targetDict = getComponentRef("remote").getDict(); + targetDict.url = form.modifyDict + ? "/mock/dicts/moreOpenStatusEnum?remote" + : "/mock/dicts/OpenStatusEnum?remote"; + targetDict.reloadDict(); + } + }, + column: { + component: { + name: "a-switch", + vModel: "checked" + }, + valueChange({ value, getComponentRef }) { + console.log("value", value); + const targetDict = getComponentRef("remote").getDict(); + targetDict.url = value ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote"; + targetDict.reloadDict(); + } + } + }, + switch: { + title: "switch", + type: "dict-switch", + dict: dict({ + data: [ + { value: true, label: "开启" }, + { value: false, label: "关闭" } + ] + }), + form: { + helper: "动态getData和动态Url根据此字段的值获取不同的dictData,此处无法动态切换,仅在打开对话框时生效" + } + }, + dynamicGetData: { + title: "动态getData", + dict: dynamicDict, + type: "dict-select" + }, + dynamicUrl: { + title: "动态Url", + dict: dynamicUrlDict, + type: "dict-select" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/dict/prototype/index.vue b/packages/ui/certd-client/src/views/crud/dict/prototype/index.vue new file mode 100644 index 000000000..1adcdfaa0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/prototype/index.vue @@ -0,0 +1,48 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/dict/prototype/mock.js b/packages/ui/certd-client/src/views/crud/dict/prototype/mock.js new file mode 100644 index 000000000..adc12b8a0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/prototype/mock.js @@ -0,0 +1,31 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "DictPrototype", + idGenerator: 0 +}; +const list = [ + { + status: "1", + remote: "2", + modifyDict: false, + switch: true, + dynamicGetData: "1", + dynamicUrl: "2" + }, + { + status: "2", + remote: "0", + modifyDict: false, + dynamicGetData: "1", + dynamicUrl: "2" + }, + { + status: "0", + modifyDict: false, + dynamicGetData: "1", + dynamicUrl: "2" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/dict/single/api.js b/packages/ui/certd-client/src/views/crud/dict/single/api.js new file mode 100644 index 000000000..971936543 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/single/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/DictSingle"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/dict/single/crud.jsx b/packages/ui/certd-client/src/views/crud/dict/single/crud.jsx new file mode 100644 index 000000000..aa161d64b --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/single/crud.jsx @@ -0,0 +1,111 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const statusDict = dict({ + cloneable: false, // 关闭cloneable,任何情况下,都使用同一个dict + data: [ + { value: "1", label: "开启", color: "success" }, + { value: "2", label: "停止", color: "blue" }, + { value: "0", label: "关闭", color: "blue" } + ] + }); + + const remoteDict = dict({ + cloneable: false, // 关闭cloneable,任何情况下,都使用同一个dict + url: "/mock/dicts/OpenStatusEnum", + immediate: false + }); + // remoteDict.loadDict(); + + return { + remoteDict, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + status: { + title: "本地字典", + search: { show: false }, + dict: statusDict, + type: "dict-select" + }, + remote: { + title: "远程字典", + search: { show: true }, + dict: remoteDict, + type: "dict-select", + column: { + component: { + onDictChange(opts) { + console.log("字典变化:", opts); + } + } + } + }, + modifyDict: { + title: "动态修改字典", + search: { show: false }, + type: "text", + form: { + component: { + name: "a-switch", + vModel: "checked" + }, + valueChange({ form }) { + console.log("changed", form.modifyDict); + remoteDict.url = form.modifyDict + ? "/mock/dicts/moreOpenStatusEnum?remote" + : "/mock/dicts/OpenStatusEnum?remote"; + // 由于remoteDict.cloneable =false,所以全局公用一个实例,修改会影响全部地方 + remoteDict.reloadDict(); + } + }, + column: { + component: { + name: "a-switch", + vModel: "checked", + on: { + // 注意:必须要on前缀 + onChange({ $event }) { + remoteDict.url = $event + ? "/mock/dicts/moreOpenStatusEnum?remote" + : "/mock/dicts/OpenStatusEnum?remote"; + remoteDict.reloadDict(); + } + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/dict/single/index.vue b/packages/ui/certd-client/src/views/crud/dict/single/index.vue new file mode 100644 index 000000000..a0f6a2d01 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/single/index.vue @@ -0,0 +1,49 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/dict/single/mock.js b/packages/ui/certd-client/src/views/crud/dict/single/mock.js new file mode 100644 index 000000000..cad375c93 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/dict/single/mock.js @@ -0,0 +1,21 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "DictSingle", + idGenerator: 0 +}; +const list = [ + { + status: "1", + remote: "2" + }, + { + status: "2", + remote: "0" + }, + { + status: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/column-resize/api.js b/packages/ui/certd-client/src/views/crud/feature/column-resize/api.js new file mode 100644 index 000000000..279509cc9 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/column-resize/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureSearch"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/column-resize/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/column-resize/crud.jsx new file mode 100644 index 000000000..913909dbd --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/column-resize/crud.jsx @@ -0,0 +1,70 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const crudBinding = expose.crudBinding; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + onResizeColumn: (w, col) => { + //触发resize事件后,修改column宽度,width只能配置为number类型 + //可以将此方法写在app.use()中的commonOptions里面 + crudBinding.value.table.columnsMap[col.key].width = w; + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50, //宽度必须number类型 + resizable: true //配置true,表示可以调整宽度 + }, + form: { + show: false + } + }, + text: { + title: "说明", + type: "text", + column: { + ellipsis: true, + showTitle: true, + resizable: true, //配置true,表示可以调整宽度 + width: 400 //宽度必须number类型 + } + }, + //必须留一个自动宽度 + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/column-resize/index.vue b/packages/ui/certd-client/src/views/crud/feature/column-resize/index.vue new file mode 100644 index 000000000..8565e9e60 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/column-resize/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/column-resize/mock.js b/packages/ui/certd-client/src/views/crud/feature/column-resize/mock.js new file mode 100644 index 000000000..68cfb67ad --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/column-resize/mock.js @@ -0,0 +1,20 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureSearch", + idGenerator: 0 +}; +const list = [ + { + text: "这一列可以调整宽度,另外必须留一列自动宽度", + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/column-sort/api.js b/packages/ui/certd-client/src/views/crud/feature/column-sort/api.js new file mode 100644 index 000000000..37c27d576 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/column-sort/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureColumnSort"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/column-sort/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/column-sort/crud.jsx new file mode 100644 index 000000000..fbadd388f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/column-sort/crud.jsx @@ -0,0 +1,94 @@ +import * as api from "./api"; +export default function ({ expose }) { + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest: api.GetList, + addRequest, + editRequest, + delRequest + }, + toolbar: { + //工具按钮排序 + buttons: { + search: { order: 1 } // 查询按钮排到前面 + } + }, + form: { + //表单跨列 + col: { span: 24 }, + labelCol: { span: 6 } + }, + columns: { + col1: { + title: "col.1", + search: { show: true }, + type: "text" + }, + col2: { + title: "col.2,我排最后一个", + search: { + //控制查询字段顺序 + show: true, + //字段默认order为100,比100大的放最后面 + order: 101 + }, + type: "text", + column: { + //控制列字段顺序 + //字段默认order为100,比100大的放最后面 + order: 101 + }, + form: { + //控制表单字段顺序 + //字段默认order为100,比100大的放最后面 + order: 101 + } + }, + col3: { + title: "col.3,我排第一个", + search: { + show: true, + order: 0 + }, + type: "text", + column: { + order: 0 + }, + form: { + order: 0 + } + }, + col4: { + title: "col.4,我在col3后面", + type: "text", + search: { + show: true, + order: 0 + }, + column: { + order: 0 + }, + form: { + order: 0 + } + }, + col5: { + title: "col.5", + type: "text" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/column-sort/index.vue b/packages/ui/certd-client/src/views/crud/feature/column-sort/index.vue new file mode 100644 index 000000000..4f86ebeab --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/column-sort/index.vue @@ -0,0 +1,47 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/column-sort/mock.js b/packages/ui/certd-client/src/views/crud/feature/column-sort/mock.js new file mode 100644 index 000000000..66315f552 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/column-sort/mock.js @@ -0,0 +1,31 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureColumnSort", + idGenerator: 0 +}; +const list = [ + { + col1: "1", + col2: "2", + col3: "3", + col4: "4", + col5: "5" + }, + { + col1: "1", + col2: "2", + col3: "3", + col4: "4", + col5: "5" + }, + { + col1: "1", + col2: "2", + col3: "3", + col4: "4", + col5: "5" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/columns-set/api.js b/packages/ui/certd-client/src/views/crud/feature/columns-set/api.js new file mode 100644 index 000000000..b3a572570 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/columns-set/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureColumnsSet"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/columns-set/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/columns-set/crud.jsx new file mode 100644 index 000000000..3099f8b95 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/columns-set/crud.jsx @@ -0,0 +1,55 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + toolbar: { + columnsFilter: { + mode: "simple" + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/columns-set/index.vue b/packages/ui/certd-client/src/views/crud/feature/columns-set/index.vue new file mode 100644 index 000000000..5c898b199 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/columns-set/index.vue @@ -0,0 +1,49 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/columns-set/mock.js b/packages/ui/certd-client/src/views/crud/feature/columns-set/mock.js new file mode 100644 index 000000000..79043c91e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/columns-set/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureColumnsSet", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/editable-row/api.js b/packages/ui/certd-client/src/views/crud/feature/editable-row/api.js new file mode 100644 index 000000000..acb0aab5c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/editable-row/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureEditableRow"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/editable-row/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/editable-row/crud.jsx new file mode 100644 index 000000000..4b4c8784b --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/editable-row/crud.jsx @@ -0,0 +1,88 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + const id = await api.AddObj(form); + return { id }; + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + actionbar: { buttons: { add: { show: false }, addRow: { show: true } } }, + table: { + editable: { + enabled: true, + mode: "row", + activeTrigger: false + } + }, + columns: { + id: { + title: "ID", + type: "number", + form: { + show: false + }, + column: { width: 80, align: "center" } + }, + disable: { + title: "禁止编辑", + type: "text", + column: { + editable: { + disabled: true //也可以配置为方法,根据条件禁用或启用编辑 + // disabled: ({ column, index, row }) => { + // return index % 2 === 0; + // } + } + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + }, + name: { + title: "姓名", + type: "text" + }, + province: { + title: "省份", + search: { show: true }, + type: "dict-select", + dict: dict({ + value: "id", + label: "text", + data: [ + { id: "sz", text: "深圳", color: "success" }, + { id: "gz", text: "广州", color: "primary" }, + { id: "bj", text: "北京" }, + { id: "wh", text: "武汉" }, + { id: "sh", text: "上海" } + ] + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/editable-row/index.vue b/packages/ui/certd-client/src/views/crud/feature/editable-row/index.vue new file mode 100644 index 000000000..54f2c6973 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/editable-row/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/editable-row/mock.js b/packages/ui/certd-client/src/views/crud/feature/editable-row/mock.js new file mode 100644 index 000000000..9a11dbb27 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/editable-row/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureEditableRow", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/editable/api.js b/packages/ui/certd-client/src/views/crud/feature/editable/api.js new file mode 100644 index 000000000..e3cf94ecc --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/editable/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureEditable"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/editable/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/editable/crud.jsx new file mode 100644 index 000000000..72d8d6084 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/editable/crud.jsx @@ -0,0 +1,105 @@ +import * as api from "./api"; +import { dict, compute } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const { crudBinding } = expose; + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + actionbar: { + buttons: { + add: { + show: compute(() => { + if (crudBinding.value) { + return !crudBinding.value?.table.editable.enabled; + } + return false; + }) + }, + addRow: { + show: compute(() => { + if (crudBinding.value) { + return crudBinding.value?.table.editable.enabled; + } + return false; + }) + } + } + }, + table: { + editable: { + mode: "free" + } + }, + columns: { + id: { + title: "ID", + type: "number", + form: { + show: false + }, + column: { width: 80, align: "center" } + }, + disable: { + title: "禁止编辑", + type: "text", + column: { + editable: { + disabled: true //也可以配置为方法,根据条件禁用或启用编辑 + // disabled: ({ column, index, row }) => { + // return index % 2 === 0; + // } + } + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + }, + name: { + title: "姓名", + type: "text" + }, + province: { + title: "省份", + search: { show: true }, + type: "dict-select", + dict: dict({ + value: "id", + label: "text", + data: [ + { id: "sz", text: "深圳", color: "success" }, + { id: "gz", text: "广州", color: "primary" }, + { id: "bj", text: "北京" }, + { id: "wh", text: "武汉" }, + { id: "sh", text: "上海" } + ] + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/editable/index.vue b/packages/ui/certd-client/src/views/crud/feature/editable/index.vue new file mode 100644 index 000000000..e7280e8a0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/editable/index.vue @@ -0,0 +1,100 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/editable/mock.js b/packages/ui/certd-client/src/views/crud/feature/editable/mock.js new file mode 100644 index 000000000..b1d6b08d6 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/editable/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureEditable", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/expand/api.js b/packages/ui/certd-client/src/views/crud/feature/expand/api.js new file mode 100644 index 000000000..c9ad003be --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/expand/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureExpand"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/expand/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/expand/crud.jsx new file mode 100644 index 000000000..5a3dee3f6 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/expand/crud.jsx @@ -0,0 +1,63 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + //默认展开第一行 + defaultExpandedRowKeys:[1], + slots: { + expandedRowRender: (scope) => { + return ( +
+ index: {scope.index} ; row: {JSON.stringify(scope.record)} +
+ ); + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/expand/index.vue b/packages/ui/certd-client/src/views/crud/feature/expand/index.vue new file mode 100644 index 000000000..fe8b3d2c4 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/expand/index.vue @@ -0,0 +1,65 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/expand/mock.js b/packages/ui/certd-client/src/views/crud/feature/expand/mock.js new file mode 100644 index 000000000..3b6d69446 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/expand/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureExpand", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/filter/api.js b/packages/ui/certd-client/src/views/crud/feature/filter/api.js new file mode 100644 index 000000000..6970dae3d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/filter/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureFilter"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/filter/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/filter/crud.jsx new file mode 100644 index 000000000..672d89113 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/filter/crud.jsx @@ -0,0 +1,70 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + // 表头过滤改变事件 + onFilterChange(e) { + console.log("onFilterChange", e); + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }), + column: { + filters: [ + { text: "开", value: "1" }, + { text: "关", value: "0" }, + { text: "停", value: "2" } + ], + // specify the condition of filtering result + // here is that finding the name started with `value` + onFilter: (value, record) => { + return record.radio === value; + }, + sorter: (a, b) => a.radio - b.radio, + sortDirections: ["descend"] + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/filter/index.vue b/packages/ui/certd-client/src/views/crud/feature/filter/index.vue new file mode 100644 index 000000000..7205a9473 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/filter/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/filter/mock.js b/packages/ui/certd-client/src/views/crud/feature/filter/mock.js new file mode 100644 index 000000000..fd405168b --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/filter/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureFilter", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/fixed/api.js b/packages/ui/certd-client/src/views/crud/feature/fixed/api.js new file mode 100644 index 000000000..ac40ccaf6 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/fixed/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureFixed"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/fixed/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/fixed/crud.jsx new file mode 100644 index 000000000..9b15c1ec1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/fixed/crud.jsx @@ -0,0 +1,90 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest: api.GetList, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + //固定右侧 + fixed: "right" + }, + table: { + scroll: { + //当你表格宽度大到需要使用固定列时,需要设置此值,并且是大于等于列宽度之和的值 + //否则可能会出现将自动宽度列挤变形,或者拖动滚动条表头不动等问题。 + x: 1400 + } + }, + columns: { + text1: { + title: "text1", + type: "text", + column: { + // 固定左侧 + // 注意被固定在左侧的列要放在最前面,否则会出现某些列错位不显示的问题 + fixed: "left", + width: 260 + } + }, + id: { + title: "id", + type: "text", + column: { + width: 100 + } + }, + text2: { + title: "text2", + type: "text", + column: { + width: 260 + } + }, + text3: { + title: "text3", + type: "text", + column: { + width: 260 + } + }, + text4: { + title: "text4", + type: "text", + column: { + width: 260 + } + }, + text5: { + title: "text5", + type: "text", + column: { + width: 260 + } + }, + last: { + title: "last", + type: "text", + column: { + width: 260 + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/fixed/index.vue b/packages/ui/certd-client/src/views/crud/feature/fixed/index.vue new file mode 100644 index 000000000..2e1d2ddfd --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/fixed/index.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/fixed/mock.js b/packages/ui/certd-client/src/views/crud/feature/fixed/mock.js new file mode 100644 index 000000000..22bd02c46 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/fixed/mock.js @@ -0,0 +1,14 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureFixed", + idGenerator: 0 +}; +const list = [ + { + text1: "我会被固定在左侧", + last: "操作列被固定在右侧" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/header-group/api.js b/packages/ui/certd-client/src/views/crud/feature/header-group/api.js new file mode 100644 index 000000000..4af20b765 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/header-group/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureHeaderGroup"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/header-group/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/header-group/crud.jsx new file mode 100644 index 000000000..09819296d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/header-group/crud.jsx @@ -0,0 +1,109 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { size: "small" }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + user: { + title: "用户信息", + children: { + name: { + title: "姓名", + type: "text" + }, + age: { + title: "年龄", + type: "number" + }, + switch: { + title: "开关", + type: "dict-switch", + dict: dict({ + data: [ + { value: true, label: "开启" }, + { value: false, label: "关闭" } + ] + }), + column: { + component: { + name: "fs-dict-switch", + vModel: "checked" + }, + valueChange(context) { + console.log("column value changed:", context); + } + } + } + } + }, + address: { + title: "地址", + children: { + area: { + title: "地区", + children: { + province: { + title: "省", + search: { show: true }, + type: "dict-select", + dict: dict({ + data: [ + { value: "广东省", label: "广东省" }, + { value: "浙江省", label: "浙江省" } + ] + }) + }, + city: { + title: "市", + search: { show: true }, + type: "text" + }, + county: { + title: "区", + search: { show: true }, + type: "text" + } + } + }, + street: { + title: "街道", + type: "text" + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/header-group/index.vue b/packages/ui/certd-client/src/views/crud/feature/header-group/index.vue new file mode 100644 index 000000000..c5b595f1c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/header-group/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/header-group/mock.js b/packages/ui/certd-client/src/views/crud/feature/header-group/mock.js new file mode 100644 index 000000000..05760a739 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/header-group/mock.js @@ -0,0 +1,30 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureHeaderGroup", + idGenerator: 0 +}; +const list = [ + { + name: "张三", + age: 18, + province: "广东省", + city: "深圳市", + county: "南山区", + street: "粤海街道" + }, + { + name: "李四", + age: 26, + province: "浙江省", + city: "杭州市", + county: "西湖区", + street: "西湖街道" + }, + { + name: "王五", + age: 24 + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/height/api.js b/packages/ui/certd-client/src/views/crud/feature/height/api.js new file mode 100644 index 000000000..4f5c7d118 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/height/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/ComponentHeight"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/height/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/height/crud.jsx new file mode 100644 index 000000000..ecd5350d9 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/height/crud.jsx @@ -0,0 +1,57 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + container: { + fixedHeight: false + }, + table: { + scroll: { + y: null + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/height/index.vue b/packages/ui/certd-client/src/views/crud/feature/height/index.vue new file mode 100644 index 000000000..34e52d62d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/height/index.vue @@ -0,0 +1,42 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/height/mock.js b/packages/ui/certd-client/src/views/crud/feature/height/mock.js new file mode 100644 index 000000000..594f66396 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/height/mock.js @@ -0,0 +1,24 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "ComponentHeight", + idGenerator: 0 +}; +const list = [ + { + radio: "1", + button: "1", + bool: true + }, + { + radio: "2", + button: "2", + bool: false + }, + { + radio: "0", + button: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/hide/api.js b/packages/ui/certd-client/src/views/crud/feature/hide/api.js new file mode 100644 index 000000000..dd73bbf3c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/hide/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureHide"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/hide/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/hide/crud.jsx new file mode 100644 index 000000000..27d53eda0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/hide/crud.jsx @@ -0,0 +1,100 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + search: { + show: true + }, + pagination: { + show: true + }, + table: { + show: true + }, + actionbar: { + show: true, + buttons: { + add: { + show: true + }, + test: { + text: "自定义按钮", + show: true, + click() { + console.log("click"); + } + } + } + }, + toolbar: { + show: true, + buttons: { + search: { show: true }, + refresh: { show: true }, + compact: { show: true }, + export: { show: true }, + columns: { show: true } + } + }, + rowHandle: { + show: true, + width: 330, + buttons: { + view: { show: true }, + edit: { show: true }, + remove: { show: true }, + custom: { + text: "自定义", + order: 4, + show: true, + click(context) { + console.log("click", context); + } + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/hide/index.vue b/packages/ui/certd-client/src/views/crud/feature/hide/index.vue new file mode 100644 index 000000000..454fa5a3e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/hide/index.vue @@ -0,0 +1,184 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/hide/mock.js b/packages/ui/certd-client/src/views/crud/feature/hide/mock.js new file mode 100644 index 000000000..6876a94c7 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/hide/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureHide", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/index/api.js b/packages/ui/certd-client/src/views/crud/feature/index/api.js new file mode 100644 index 000000000..4cf33ea3a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/index/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureIndex"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/index/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/index/crud.jsx new file mode 100644 index 000000000..5c3f31050 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/index/crud.jsx @@ -0,0 +1,56 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: {}, + columns: { + _index: { + title: "序号", + form: { show: false }, + column: { + // type: "index", + align: "center", + width: "55px", + columnSetDisabled: true, //禁止在列设置中选择 + formatter: (context) => { + //计算序号,你可以自定义计算规则,此处为翻页累加 + let index = context.index ?? 1; + let pagination = expose.crudBinding.value.pagination; + return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1; + } + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/index/index.vue b/packages/ui/certd-client/src/views/crud/feature/index/index.vue new file mode 100644 index 000000000..06c33b2b8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/index/index.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/index/mock.js b/packages/ui/certd-client/src/views/crud/feature/index/mock.js new file mode 100644 index 000000000..6e10439f4 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/index/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureIndex", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/local-v-model/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/local-v-model/crud.jsx new file mode 100644 index 000000000..1dbe46ff5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/local-v-model/crud.jsx @@ -0,0 +1,32 @@ +import { uiContext } from "@fast-crud/fast-crud"; + +export default function ({ expose }) { + return { + crudOptions: { + mode:{ + name:'local', + isMergeWhenUpdate:true, + isAppendWhenAdd:true + }, + search: { + show: false + }, + toolbar:{ + show:false, + }, + pagination:{ + show:false + }, + columns: { + name: { + type: "text", + title: "联系人姓名" + }, + mobile: { + type: "text", + title: "联系人手机号码" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/local-v-model/index.vue b/packages/ui/certd-client/src/views/crud/feature/local-v-model/index.vue new file mode 100644 index 000000000..01e615f59 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/local-v-model/index.vue @@ -0,0 +1,51 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/local-v-model/local.vue b/packages/ui/certd-client/src/views/crud/feature/local-v-model/local.vue new file mode 100644 index 000000000..0110ae279 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/local-v-model/local.vue @@ -0,0 +1,67 @@ + + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/local/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/local/crud.jsx new file mode 100644 index 000000000..c0f2f6af8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/local/crud.jsx @@ -0,0 +1,35 @@ +import { uiContext } from "@fast-crud/fast-crud"; + +export default function ({ expose }) { + return { + crudOptions: { + mode: { + name: "local", + isMergeWhenUpdate: true, + isAppendWhenAdd: true + }, + actionbar: { buttons: { add: { show: true }, addRow: { show: false } } }, + editable: { + enabled: false, + mode: "row", + activeTrigger: false + }, + search: { + show: false + }, + pagination: { + show: false + }, + columns: { + name: { + type: "text", + title: "联系人姓名" + }, + mobile: { + type: "text", + title: "联系人手机号码" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/local/index.vue b/packages/ui/certd-client/src/views/crud/feature/local/index.vue new file mode 100644 index 000000000..743588f1a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/local/index.vue @@ -0,0 +1,89 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/merge/api.js b/packages/ui/certd-client/src/views/crud/feature/merge/api.js new file mode 100644 index 000000000..65dafffca --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/merge/api.js @@ -0,0 +1,43 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureMerge"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + diff --git a/packages/ui/certd-client/src/views/crud/feature/merge/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/merge/crud.jsx new file mode 100644 index 000000000..36d418971 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/merge/crud.jsx @@ -0,0 +1,129 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + /** + * 列合并render + */ + function colMergeRender({ index }) { + return { + props: { + colSpan: 5 + } + }; + } + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + slots: { + summary() { + return
总结栏
; + } + } + }, + columns: { + id: { + title: "id", + type: "text" + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + }, + cellMerge: { + title: "上下合并", + column: { + customRender: ({ text, index }, cellRender) => { + const obj = { + props: {} + }; + if (index === 2) { + obj.children = text + "(我合并了)"; + obj.props.rowSpan = 2; + } else if (index === 3) { + obj.props.rowSpan = 0; + } else { + obj.children = cellRender(); + } + return obj; + } + } + }, + colMerge1: { + title: "左右合并", + column: { + align: "center", + customRender({ text, index, record, dataIndex }, cellRender) { + if (index !== 4) { + return { + children: cellRender() + }; + } + return { + children: text + "(我合并了)", + props: { + colSpan: 2 + } + }; + } + } + }, + colMerge2: { + title: "左右合并", + column: { + customRender({ text, index, record, dataIndex }, cellRender) { + if (index !== 4) { + return { + children: cellRender() + }; + } + return { + props: { + colSpan: 0 + } + }; + } + } + }, + header1: { + title: "表头合并(我合并了)", + type: "text", + column: { + colSpan: 2 + } + }, + header2: { + title: "表头合并", + type: "text", + column: { + colSpan: 0 + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/merge/index.vue b/packages/ui/certd-client/src/views/crud/feature/merge/index.vue new file mode 100644 index 000000000..49d0ce99c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/merge/index.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/merge/mock.js b/packages/ui/certd-client/src/views/crud/feature/merge/mock.js new file mode 100644 index 000000000..e99a6af50 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/merge/mock.js @@ -0,0 +1,36 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureMerge", + idGenerator: 0 +}; +const list = [ + { + radio: "1", + cellMerge: "test", + colMerge1: "111", + colMerge2: "222", + + header1: "aaa", + header2: "bbb" + }, + { + radio: "2", + cellMerge: "test", + colMerge1: "111", + colMerge2: "222", + + header1: "aaa", + header2: "bbb" + }, + { + radio: "0", + cellMerge: "test", + colMerge1: "111", + colMerge2: "222", + header1: "aaa", + header2: "bbb" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/remove/api.js b/packages/ui/certd-client/src/views/crud/feature/remove/api.js new file mode 100644 index 000000000..1d251eb79 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/remove/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureRemove"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/remove/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/remove/crud.jsx new file mode 100644 index 000000000..52a476cd3 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/remove/crud.jsx @@ -0,0 +1,74 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { Modal } from "ant-design-vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + remove: { + async confirmFn(context) { + await new Promise((resolve, reject) => { + Modal.confirm({ + content: `确定删除记录(${context.row.id})吗?`, + onOk() { + resolve(); + }, + onCancel() { + reject(); + } + }); + }); + }, + confirmTitle: "请确认", // confirmFn配置为空时生效 + confirmMessage: "确定删除此记录吗", // confirmFn配置为空时生效 + showSuccessMessage: true, //是否显示删除成功记录 + refreshTable: true, //删除后刷新表格 + onCanceled({ row }) { + console.log(`记录${row.id}取消删除`); + }, + onRemoved({ row }) { + console.log(`记录${row.id}已删除`); + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + text: { + title: "文本", + search: { show: true }, + type: "text" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/remove/index.vue b/packages/ui/certd-client/src/views/crud/feature/remove/index.vue new file mode 100644 index 000000000..804e85bb1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/remove/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/remove/mock.js b/packages/ui/certd-client/src/views/crud/feature/remove/mock.js new file mode 100644 index 000000000..efb116af7 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/remove/mock.js @@ -0,0 +1,25 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureRemove", + idGenerator: 0 +}; +const list = [ + { + text: "测试文本1" + }, + { + text: "测试文本2" + }, + { + text: "测试文本3" + }, + { + text: "测试文本4" + }, + { + text: "测试文本5" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/search-multi/api.js b/packages/ui/certd-client/src/views/crud/feature/search-multi/api.js new file mode 100644 index 000000000..2f83742af --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/search-multi/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureSearchMulti"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/search-multi/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/search-multi/crud.jsx new file mode 100644 index 000000000..6f9fdbd2f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/search-multi/crud.jsx @@ -0,0 +1,143 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + search: { + layout: "multi-line", + col: { + span: 4 + }, + options: { + labelCol: { + style: { + width: "100px" + } + } + } + }, + actionbar: { + buttons: { + change: { + text: "切换模式", + click() { + if (expose.crudBinding.value.search.layout === "multi-line") { + expose.crudBinding.value.search.layout = ""; + } else { + expose.crudBinding.value.search.layout = "multi-line"; + } + } + }, + search: { + text: "查询", + click() { + expose.getSearchRef().doSearch(); + } + }, + reset: { + text: "重置查询", + click() { + expose.getSearchRef().doReset(); + } + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }), + column: { + filters: [ + { text: "开", value: "1" }, + { text: "关", value: "0" }, + { text: "停", value: "2" } + ], + // specify the condition of filtering result + // here is that finding the name started with `value` + onFilter: (value, record) => { + return record.radio === value; + }, + sorter: (a, b) => a.radio - b.radio, + sortDirections: ["descend"] + } + }, + text1: { + type: "text", + title: "text1", + search: { show: true } + }, + text2: { + type: "text", + title: "text2", + search: { show: true } + }, + text3: { + type: "text", + title: "text3", + search: { show: true } + }, + text4: { + type: "text", + title: "text4", + search: { show: true } + }, + text5: { + type: "text", + title: "text5", + search: { show: true } + }, + text6: { + type: "text", + title: "text6", + search: { show: true } + }, + text7: { + type: "text", + title: "text7", + search: { show: true } + }, + text8: { + type: "text", + title: "text8", + search: { show: true } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/search-multi/index.vue b/packages/ui/certd-client/src/views/crud/feature/search-multi/index.vue new file mode 100644 index 000000000..6e6d96052 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/search-multi/index.vue @@ -0,0 +1,41 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/search-multi/mock.js b/packages/ui/certd-client/src/views/crud/feature/search-multi/mock.js new file mode 100644 index 000000000..0a2531527 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/search-multi/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureSearchMulti", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/search/api.js b/packages/ui/certd-client/src/views/crud/feature/search/api.js new file mode 100644 index 000000000..279509cc9 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/search/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureSearch"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/search/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/search/crud.jsx new file mode 100644 index 000000000..0fe249de4 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/search/crud.jsx @@ -0,0 +1,88 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + // 表头过滤改变事件 + onFilterChange(e) { + console.log("onFilterChange", e); + } + }, + search:{ + buttons:{ + custom:{ + text:"自定义", + show:true, + order:3, + icon:{ + icon:"ant-design:search", + style:{ + "font-size":'16px' + } + }, + click(){ + console.log("点击了自定义按钮") + } + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }), + column: { + filters: [ + { text: "开", value: "1" }, + { text: "关", value: "0" }, + { text: "停", value: "2" } + ], + // specify the condition of filtering result + // here is that finding the name started with `value` + onFilter: (value, record) => { + return record.radio === value; + }, + sorter: (a, b) => a.radio - b.radio, + sortDirections: ["descend"] + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/search/index.vue b/packages/ui/certd-client/src/views/crud/feature/search/index.vue new file mode 100644 index 000000000..e36b8dff1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/search/index.vue @@ -0,0 +1,57 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/search/mock.js b/packages/ui/certd-client/src/views/crud/feature/search/mock.js new file mode 100644 index 000000000..a6741fa06 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/search/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureSearch", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/selection-radio/api.js b/packages/ui/certd-client/src/views/crud/feature/selection-radio/api.js new file mode 100644 index 000000000..1d86caa21 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/selection-radio/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureSelection"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/selection-radio/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/selection-radio/crud.jsx new file mode 100644 index 000000000..18efb25f8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/selection-radio/crud.jsx @@ -0,0 +1,67 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const selectedRowKey = ref(); + const onSelectChange = (changed) => { + console.log("selection", changed); + selectedRowKey.value = changed; + }; + return { + selectedRowKey, //返回给index.vue去使用 + crudOptions: { + table: { + rowKey: "id", + rowSelection: { + type: "radio", + selectedRowKeys: selectedRowKey, + onChange: onSelectChange, + getCheckboxProps: (record) => ({ + disabled: record.id === 1 // 此处演示第一行禁用 + }) + } + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/selection-radio/index.vue b/packages/ui/certd-client/src/views/crud/feature/selection-radio/index.vue new file mode 100644 index 000000000..57386c5a6 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/selection-radio/index.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/selection-radio/mock.js b/packages/ui/certd-client/src/views/crud/feature/selection-radio/mock.js new file mode 100644 index 000000000..b9b0bade6 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/selection-radio/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureSelection", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/selection/api.js b/packages/ui/certd-client/src/views/crud/feature/selection/api.js new file mode 100644 index 000000000..1d86caa21 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/selection/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureSelection"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/selection/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/selection/crud.jsx new file mode 100644 index 000000000..9ac917e3b --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/selection/crud.jsx @@ -0,0 +1,67 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const selectedRowKeys = ref([]); + + const onSelectChange = (changed) => { + console.log("selection", changed); + selectedRowKeys.value = changed; + }; + return { + selectedRowKeys, //返回给index.vue去使用 + crudOptions: { + table: { + rowKey: "id", + rowSelection: { + selectedRowKeys: selectedRowKeys, + onChange: onSelectChange, + getCheckboxProps: (record) => ({ + disabled: record.id === 1 // 此处演示第一行禁用 + }) + } + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/selection/index.vue b/packages/ui/certd-client/src/views/crud/feature/selection/index.vue new file mode 100644 index 000000000..7a84d6c29 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/selection/index.vue @@ -0,0 +1,68 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/selection/mock.js b/packages/ui/certd-client/src/views/crud/feature/selection/mock.js new file mode 100644 index 000000000..b9b0bade6 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/selection/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureSelection", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/sortable/api.js b/packages/ui/certd-client/src/views/crud/feature/sortable/api.js new file mode 100644 index 000000000..17fe338f5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/sortable/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureSortable"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/sortable/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/sortable/crud.jsx new file mode 100644 index 000000000..0b00b387a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/sortable/crud.jsx @@ -0,0 +1,62 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest: api.GetList, + addRequest, + editRequest, + delRequest + }, + columns: { + radio: { + title: "本地排序", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }), + column: { + sorter(a, b) { + return a.radio < b.radio ? 1 : -1; + } + } + }, + radio1: { + title: "服务端排序1", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }), + column: { + sorter: true + } + }, + radio2: { + title: "服务端排序2", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }), + column: { + sorter: true + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/sortable/index.vue b/packages/ui/certd-client/src/views/crud/feature/sortable/index.vue new file mode 100644 index 000000000..1d3e59455 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/sortable/index.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/sortable/mock.js b/packages/ui/certd-client/src/views/crud/feature/sortable/mock.js new file mode 100644 index 000000000..5bb6d0e69 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/sortable/mock.js @@ -0,0 +1,25 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureSortable", + idGenerator: 0 +}; +const list = [ + { + radio: "1", + radio1: "1", + radio2: "2" + }, + { + radio: "2", + radio1: "2", + radio2: "0" + }, + { + radio: "0", + radio1: "0", + radio2: "1" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/tree/api.js b/packages/ui/certd-client/src/views/crud/feature/tree/api.js new file mode 100644 index 000000000..771633983 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/tree/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureTree"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/tree/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/tree/crud.jsx new file mode 100644 index 000000000..b47c3fa96 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/tree/crud.jsx @@ -0,0 +1,91 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; + +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const selectedRowKeys = ref([]); + + const onSelectChange = (changed) => { + console.log("selection", changed); + selectedRowKeys.value = changed; + }; + return { + selectedRowKeys, //返回给index.vue去使用 + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + table: { + rowSelection: { selectedRowKeys: selectedRowKeys, onChange: onSelectChange } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 100 + }, + form: { + show: false + } + }, + time: { + title: "时间", + type: "datetime", + column: { + width: 180 + } + }, + province: { + title: "地区", + type: "dict-select", + search: { show: true }, + form: { + component: { filterable: true, multiple: true } + }, + dict: dict({ + data: [ + { value: "sz", label: "深圳" }, + { value: "gz", label: "广州" }, + { value: "wh", label: "武汉" }, + { value: "sh", label: "上海" } + ] + }), + column: { + width: 300 + } + }, + amount: { + title: "金额(元)", + key: "amount" + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/tree/index.vue b/packages/ui/certd-client/src/views/crud/feature/tree/index.vue new file mode 100644 index 000000000..b9240eac1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/tree/index.vue @@ -0,0 +1,65 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/tree/mock.js b/packages/ui/certd-client/src/views/crud/feature/tree/mock.js new file mode 100644 index 000000000..f1df3badc --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/tree/mock.js @@ -0,0 +1,85 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureTree", + idGenerator: 0 +}; +const list = [ + { + radio: "1", + data: "我会懒加载", + time: "2020-01-01 11:11:11", + province: "wh", + amount: 100, + hasChildren: true, + loaded: false, + children: [ + { + data: "懒加载的子数据", + province: ["sh", "gz"], + time: "2020-01-01 11:11:11", + amount: 100 + }, + { + data: "懒加载的子数据2", + province: ["sh", "sz"], + time: "2020-01-01 11:11:11", + amount: 100 + } + ] + }, + { + data: "data2", + province: "sh", + time: "2020-01-01 11:11:11", + amount: 100, + children: [ + { + id: 999, + data: "data1_1", + time: "2020-01-01 11:11:11", + province: ["gz", "sz"], // 可以逗号分隔的字符串 'gz,sz' + amount: 100, + children: [ + { + id: 1000, + data: "data1_1_1", + time: "2020-01-01 11:11:11", + province: ["sz", "gz"], // 可以逗号分隔的字符串 'gz,sz' + amount: 100 + } + ] + }, + { + id: 888, + data: "data1_2", + time: "2020-01-01 11:11:11", + province: "sh", + amount: 100, + children: [ + { + id: 889, + data: "data1_2_1", + time: "2020-01-01 11:11:11", + province: "gz", + amount: 100 + } + ] + } + ] + }, + { + data: "data3", + province: ["sh", "gz"], + time: "2020-01-01 11:11:11", + amount: 100 + }, + { + data: "data4", + province: ["sh", "sz"], + time: "2020-01-01 11:11:11", + amount: 100 + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/feature/value-builder/api.js b/packages/ui/certd-client/src/views/crud/feature/value-builder/api.js new file mode 100644 index 000000000..09e547c6a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/value-builder/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FeatureValueBuilder"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/feature/value-builder/crud.jsx b/packages/ui/certd-client/src/views/crud/feature/value-builder/crud.jsx new file mode 100644 index 000000000..b44f24830 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/value-builder/crud.jsx @@ -0,0 +1,54 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest: api.GetList, + addRequest, + editRequest, + delRequest + }, + columns: { + name: { + title: "姓名", + type: "text" + }, + roles: { + title: "角色", + search: { show: true }, + type: "dict-select", + dict: dict({ + value: "id", + label: "name", + data: [ + { id: 1, name: "管理员" }, + { id: 2, name: "普通用户" } + ] + }), + form: { + component: { + mode: "multiple" + }, + valueBuilder({ form }) { + if (form.roles) { + form.roles = form.roles.map((item) => item.id); + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/feature/value-builder/index.vue b/packages/ui/certd-client/src/views/crud/feature/value-builder/index.vue new file mode 100644 index 000000000..156b4e5bf --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/value-builder/index.vue @@ -0,0 +1,46 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/feature/value-builder/mock.js b/packages/ui/certd-client/src/views/crud/feature/value-builder/mock.js new file mode 100644 index 000000000..8c86a37e1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/feature/value-builder/mock.js @@ -0,0 +1,21 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "FeatureValueBuilder", + idGenerator: 0 +}; +const list = [ + { + name: "user1", + roles: [ + { id: 1, name: "管理员" }, + { id: 2, name: "普通用户" } + ] + }, + { + name: "user2", + roles: [{ id: 1, name: "管理员" }] + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/base/api.js b/packages/ui/certd-client/src/views/crud/form/base/api.js new file mode 100644 index 000000000..cff9ab911 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/base/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormBase"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/base/crud.jsx b/packages/ui/certd-client/src/views/crud/form/base/crud.jsx new file mode 100644 index 000000000..453a68296 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/base/crud.jsx @@ -0,0 +1,33 @@ +import * as api from "./api"; +export default function ({ crudExpose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + name: { + title: "姓名", + type: "text" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/base/index.vue b/packages/ui/certd-client/src/views/crud/form/base/index.vue new file mode 100644 index 000000000..97184909d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/base/index.vue @@ -0,0 +1,32 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/base/mock.js b/packages/ui/certd-client/src/views/crud/form/base/mock.js new file mode 100644 index 000000000..bb0fe7b99 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/base/mock.js @@ -0,0 +1,310 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormBase", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + age: 15, + password: "", + status: "2", + url: "https://baidu.com" + }, + { + name: "张三", + age: 18, + password: "", + url: "https://baidu.com" + }, + { + status: "2" + } +]; + +const dictData = [ + { + value: "zhinan", + label: "指南", + children: [ + { + value: "shejiyuanze", + label: "设计原则", + children: [ + { + value: "yizhi", + label: "一致" + }, + { + value: "fankui", + label: "反馈" + }, + { + value: "xiaolv", + label: "效率" + }, + { + value: "kekong", + label: "可控" + } + ] + }, + { + value: "daohang", + label: "导航", + children: [ + { + value: "cexiangdaohang", + label: "侧向导航" + }, + { + value: "dingbudaohang", + label: "顶部导航" + } + ] + } + ] + }, + { + value: "zujian", + label: "组件", + children: [ + { + value: "basic", + label: "Basic", + children: [ + { + value: "layout", + label: "Layout 布局" + }, + { + value: "color", + label: "Color 色彩" + }, + { + value: "typography", + label: "Typography 字体" + }, + { + value: "icon", + label: "Icon 图标" + }, + { + value: "button", + label: "Button 按钮" + } + ] + }, + { + value: "form", + label: "Form", + children: [ + { + value: "radio", + label: "Radio 单选框" + }, + { + value: "checkbox", + label: "Checkbox 多选框" + }, + { + value: "input", + label: "Input 输入框" + }, + { + value: "input-number", + label: "InputNumber 计数器" + }, + { + value: "select", + label: "Select 选择器" + }, + { + value: "cascader", + label: "Cascader 级联选择器" + }, + { + value: "switch", + label: "Switch 开关" + }, + { + value: "slider", + label: "Slider 滑块" + }, + { + value: "time-picker", + label: "TimePicker 时间选择器" + }, + { + value: "date-picker", + label: "DatePicker 日期选择器" + }, + { + value: "datetime-picker", + label: "DateTimePicker 日期时间选择器" + }, + { + value: "upload", + label: "Upload 上传" + }, + { + value: "rate", + label: "Rate 评分" + }, + { + value: "form", + label: "Form 表单" + } + ] + }, + { + value: "data", + label: "Data", + children: [ + { + value: "table", + label: "Table 表格" + }, + { + value: "tag", + label: "Tag 标签" + }, + { + value: "progress", + label: "Progress 进度条" + }, + { + value: "tree", + label: "Tree 树形控件" + }, + { + value: "pagination", + label: "Pagination 分页" + }, + { + value: "badge", + label: "Badge 标记" + } + ] + }, + { + value: "notice", + label: "Notice", + children: [ + { + value: "alert", + label: "Alert 警告" + }, + { + value: "loading", + label: "Loading 加载" + }, + { + value: "message", + label: "Message 消息提示" + }, + { + value: "message-box", + label: "MessageBox 弹框" + }, + { + value: "notification", + label: "Notification 通知" + } + ] + }, + { + value: "navigation", + label: "Navigation", + children: [ + { + value: "menu", + label: "NavMenu 导航菜单" + }, + { + value: "tabs", + label: "Tabs 标签页" + }, + { + value: "breadcrumb", + label: "Breadcrumb 面包屑" + }, + { + value: "dropdown", + label: "Dropdown 下拉菜单" + }, + { + value: "steps", + label: "Steps 步骤条" + } + ] + }, + { + value: "others", + label: "Others", + children: [ + { + value: "dialog", + label: "Dialog 对话框" + }, + { + value: "tooltip", + label: "Tooltip 文字提示" + }, + { + value: "popover", + label: "Popover 弹出框" + }, + { + value: "card", + label: "Card 卡片" + }, + { + value: "carousel", + label: "Carousel 走马灯" + }, + { + value: "collapse", + label: "Collapse 折叠面板" + } + ] + } + ] + }, + { + value: "ziyuan", + label: "资源", + children: [ + { + value: "axure", + label: "Axure Components" + }, + { + value: "sketch", + label: "Sketch Templates" + }, + { + value: "jiaohu", + label: "组件交互文档" + } + ] + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +mock.push({ + path: "/select/cascadeData", + method: "get", + handle(req) { + return { + code: 0, + msg: "success", + data: dictData + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/custom-form/api.js b/packages/ui/certd-client/src/views/crud/form/custom-form/api.js new file mode 100644 index 000000000..1cab36020 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/custom-form/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormCustomForm"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/custom-form/crud.jsx b/packages/ui/certd-client/src/views/crud/form/custom-form/crud.jsx new file mode 100644 index 000000000..875a80636 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/custom-form/crud.jsx @@ -0,0 +1,40 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const { getFormRef, getFormData } = expose; + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + title: { + title: "商品标题", + type: "text" + }, + code: { + title: "商品代码", + search: { show: true }, + type: "text" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/custom-form/index.vue b/packages/ui/certd-client/src/views/crud/form/custom-form/index.vue new file mode 100644 index 000000000..faa3c2be1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/custom-form/index.vue @@ -0,0 +1,86 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/custom-form/mock.js b/packages/ui/certd-client/src/views/crud/form/custom-form/mock.js new file mode 100644 index 000000000..479b89b43 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/custom-form/mock.js @@ -0,0 +1,303 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormCustomForm", + idGenerator: 0 +}; +const list = [ + { + title: "无线充电宝", + code: "100001", + images: "https://img0.bdstatic.com/static/searchdetail/img/logo-2X_0c4ef02.png", + price: 100, + stock: 99, + intro: "30000毫安超大容量移动电源充电宝官方原装正品专用便携", + content: "" + } +]; + +const dictData = [ + { + value: "zhinan", + label: "指南", + children: [ + { + value: "shejiyuanze", + label: "设计原则", + children: [ + { + value: "yizhi", + label: "一致" + }, + { + value: "fankui", + label: "反馈" + }, + { + value: "xiaolv", + label: "效率" + }, + { + value: "kekong", + label: "可控" + } + ] + }, + { + value: "daohang", + label: "导航", + children: [ + { + value: "cexiangdaohang", + label: "侧向导航" + }, + { + value: "dingbudaohang", + label: "顶部导航" + } + ] + } + ] + }, + { + value: "zujian", + label: "组件", + children: [ + { + value: "basic", + label: "Basic", + children: [ + { + value: "layout", + label: "Layout 布局" + }, + { + value: "color", + label: "Color 色彩" + }, + { + value: "typography", + label: "Typography 字体" + }, + { + value: "icon", + label: "Icon 图标" + }, + { + value: "button", + label: "Button 按钮" + } + ] + }, + { + value: "form", + label: "Form", + children: [ + { + value: "radio", + label: "Radio 单选框" + }, + { + value: "checkbox", + label: "Checkbox 多选框" + }, + { + value: "input", + label: "Input 输入框" + }, + { + value: "input-number", + label: "InputNumber 计数器" + }, + { + value: "select", + label: "Select 选择器" + }, + { + value: "cascader", + label: "Cascader 级联选择器" + }, + { + value: "switch", + label: "Switch 开关" + }, + { + value: "slider", + label: "Slider 滑块" + }, + { + value: "time-picker", + label: "TimePicker 时间选择器" + }, + { + value: "date-picker", + label: "DatePicker 日期选择器" + }, + { + value: "datetime-picker", + label: "DateTimePicker 日期时间选择器" + }, + { + value: "upload", + label: "Upload 上传" + }, + { + value: "rate", + label: "Rate 评分" + }, + { + value: "form", + label: "Form 表单" + } + ] + }, + { + value: "data", + label: "Data", + children: [ + { + value: "table", + label: "Table 表格" + }, + { + value: "tag", + label: "Tag 标签" + }, + { + value: "progress", + label: "Progress 进度条" + }, + { + value: "tree", + label: "Tree 树形控件" + }, + { + value: "pagination", + label: "Pagination 分页" + }, + { + value: "badge", + label: "Badge 标记" + } + ] + }, + { + value: "notice", + label: "Notice", + children: [ + { + value: "alert", + label: "Alert 警告" + }, + { + value: "loading", + label: "Loading 加载" + }, + { + value: "message", + label: "Message 消息提示" + }, + { + value: "message-box", + label: "MessageBox 弹框" + }, + { + value: "notification", + label: "Notification 通知" + } + ] + }, + { + value: "navigation", + label: "Navigation", + children: [ + { + value: "menu", + label: "NavMenu 导航菜单" + }, + { + value: "tabs", + label: "Tabs 标签页" + }, + { + value: "breadcrumb", + label: "Breadcrumb 面包屑" + }, + { + value: "dropdown", + label: "Dropdown 下拉菜单" + }, + { + value: "steps", + label: "Steps 步骤条" + } + ] + }, + { + value: "others", + label: "Others", + children: [ + { + value: "dialog", + label: "Dialog 对话框" + }, + { + value: "tooltip", + label: "Tooltip 文字提示" + }, + { + value: "popover", + label: "Popover 弹出框" + }, + { + value: "card", + label: "Card 卡片" + }, + { + value: "carousel", + label: "Carousel 走马灯" + }, + { + value: "collapse", + label: "Collapse 折叠面板" + } + ] + } + ] + }, + { + value: "ziyuan", + label: "资源", + children: [ + { + value: "axure", + label: "Axure Components" + }, + { + value: "sketch", + label: "Sketch Templates" + }, + { + value: "jiaohu", + label: "组件交互文档" + } + ] + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +mock.push({ + path: "/select/cascadeData", + method: "get", + handle(req) { + return { + code: 0, + msg: "success", + data: dictData + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/drawer/api.js b/packages/ui/certd-client/src/views/crud/form/drawer/api.js new file mode 100644 index 000000000..b9e50dc64 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/drawer/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/formText"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/drawer/crud.jsx b/packages/ui/certd-client/src/views/crud/form/drawer/crud.jsx new file mode 100644 index 000000000..bf6e0c5fa --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/drawer/crud.jsx @@ -0,0 +1,44 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + wrapper: { + is: "a-drawer" + } + }, + columns: { + name: { + title: "姓名", + type: "text", //虽然不写也能正确显示组件,但不建议省略它 + search: { show: true }, + form: { + component: { + maxlength: 20 + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/drawer/index.vue b/packages/ui/certd-client/src/views/crud/form/drawer/index.vue new file mode 100644 index 000000000..5143f074e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/drawer/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/drawer/mock.js b/packages/ui/certd-client/src/views/crud/form/drawer/mock.js new file mode 100644 index 000000000..fcf01621e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/drawer/mock.js @@ -0,0 +1,40 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "formText", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + date: "2016-05-02", + status: "0", + province: "1", + avatar: "https://alicdn.antdv.com/vue.png", + show: true, + city: "sz", + address: "123123", + zip: "518000", + intro: "王小虎是element-plus的table示例出现的名字" + }, + { + name: "张三", + date: "2016-05-04", + status: "1", + province: "2" + }, + { + name: "李四", + date: 2232433534511, + status: "1", + province: "0" + }, + { + name: "王五", + date: "2016-05-03", + status: "2", + province: "wh,gz" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/group-tabs/api.js b/packages/ui/certd-client/src/views/crud/form/group-tabs/api.js new file mode 100644 index 000000000..a059b9001 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/group-tabs/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormGroupTabs"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/group-tabs/crud.jsx b/packages/ui/certd-client/src/views/crud/form/group-tabs/crud.jsx new file mode 100644 index 000000000..74dbfb0b3 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/group-tabs/crud.jsx @@ -0,0 +1,114 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const { getFormRef, getFormData } = expose; + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + title: { + title: "商品标题", + type: "text" + }, + code: { + title: "商品代码", + search: { show: true }, + type: "text", + form: { + rules: [{ required: true, message: "此项必填" }] + } + }, + images: { + title: "图片", + type: "image-uploader" + }, + price: { + title: "价格", + sortable: true + }, + store: { + title: "库存", + type: "number" + }, + intro: { + title: "简介", + type: "textarea", + column: { + ellipsis: true, + showTitle: true + } + }, + content: { + title: "详情", + type: "editor-ueditor", + form: { + labelWidth: "0px" + } + } + }, + form: { + group: { + groupType: "tabs", //collapse, tabs + accordion: false, + groups: { + base: { + slots: { + tab: (scope) => { + return ( + + + 商品基础 + + ); + } + }, + icon: "el-icon-goods", + columns: ["code", "title", "images"] + }, + price: { + tab: "库存价格", + icon: "el-icon-price-tag", + columns: ["store", "price"] + }, + info: { + tab: "详情", + collapsed: true, //默认折叠 + icon: "el-icon-warning-outline", + columns: ["intro", "content"] + } + // custom: { + // title: "自定义", + // collapsed: false, + // show(context) { + // console.log("custom context", context); + // return context.mode === "view"; + // }, + // disabled: false, + // icon: "el-icon-warning-outline", + // columns: ["custom", "custom2"] + // } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/group-tabs/index.vue b/packages/ui/certd-client/src/views/crud/form/group-tabs/index.vue new file mode 100644 index 000000000..3d65edece --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/group-tabs/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/group-tabs/mock.js b/packages/ui/certd-client/src/views/crud/form/group-tabs/mock.js new file mode 100644 index 000000000..bc980fa87 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/group-tabs/mock.js @@ -0,0 +1,303 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormGroupTabs", + idGenerator: 0 +}; +const list = [ + { + title: "无线充电宝", + code: "100001", + images: "https://img0.bdstatic.com/static/searchdetail/img/logo-2X_0c4ef02.png", + price: 100, + stock: 99, + intro: "30000毫安超大容量移动电源充电宝官方原装正品专用便携", + content: "" + } +]; + +const dictData = [ + { + value: "zhinan", + label: "指南", + children: [ + { + value: "shejiyuanze", + label: "设计原则", + children: [ + { + value: "yizhi", + label: "一致" + }, + { + value: "fankui", + label: "反馈" + }, + { + value: "xiaolv", + label: "效率" + }, + { + value: "kekong", + label: "可控" + } + ] + }, + { + value: "daohang", + label: "导航", + children: [ + { + value: "cexiangdaohang", + label: "侧向导航" + }, + { + value: "dingbudaohang", + label: "顶部导航" + } + ] + } + ] + }, + { + value: "zujian", + label: "组件", + children: [ + { + value: "basic", + label: "Basic", + children: [ + { + value: "layout", + label: "Layout 布局" + }, + { + value: "color", + label: "Color 色彩" + }, + { + value: "typography", + label: "Typography 字体" + }, + { + value: "icon", + label: "Icon 图标" + }, + { + value: "button", + label: "Button 按钮" + } + ] + }, + { + value: "form", + label: "Form", + children: [ + { + value: "radio", + label: "Radio 单选框" + }, + { + value: "checkbox", + label: "Checkbox 多选框" + }, + { + value: "input", + label: "Input 输入框" + }, + { + value: "input-number", + label: "InputNumber 计数器" + }, + { + value: "select", + label: "Select 选择器" + }, + { + value: "cascader", + label: "Cascader 级联选择器" + }, + { + value: "switch", + label: "Switch 开关" + }, + { + value: "slider", + label: "Slider 滑块" + }, + { + value: "time-picker", + label: "TimePicker 时间选择器" + }, + { + value: "date-picker", + label: "DatePicker 日期选择器" + }, + { + value: "datetime-picker", + label: "DateTimePicker 日期时间选择器" + }, + { + value: "upload", + label: "Upload 上传" + }, + { + value: "rate", + label: "Rate 评分" + }, + { + value: "form", + label: "Form 表单" + } + ] + }, + { + value: "data", + label: "Data", + children: [ + { + value: "table", + label: "Table 表格" + }, + { + value: "tag", + label: "Tag 标签" + }, + { + value: "progress", + label: "Progress 进度条" + }, + { + value: "tree", + label: "Tree 树形控件" + }, + { + value: "pagination", + label: "Pagination 分页" + }, + { + value: "badge", + label: "Badge 标记" + } + ] + }, + { + value: "notice", + label: "Notice", + children: [ + { + value: "alert", + label: "Alert 警告" + }, + { + value: "loading", + label: "Loading 加载" + }, + { + value: "message", + label: "Message 消息提示" + }, + { + value: "message-box", + label: "MessageBox 弹框" + }, + { + value: "notification", + label: "Notification 通知" + } + ] + }, + { + value: "navigation", + label: "Navigation", + children: [ + { + value: "menu", + label: "NavMenu 导航菜单" + }, + { + value: "tabs", + label: "Tabs 标签页" + }, + { + value: "breadcrumb", + label: "Breadcrumb 面包屑" + }, + { + value: "dropdown", + label: "Dropdown 下拉菜单" + }, + { + value: "steps", + label: "Steps 步骤条" + } + ] + }, + { + value: "others", + label: "Others", + children: [ + { + value: "dialog", + label: "Dialog 对话框" + }, + { + value: "tooltip", + label: "Tooltip 文字提示" + }, + { + value: "popover", + label: "Popover 弹出框" + }, + { + value: "card", + label: "Card 卡片" + }, + { + value: "carousel", + label: "Carousel 走马灯" + }, + { + value: "collapse", + label: "Collapse 折叠面板" + } + ] + } + ] + }, + { + value: "ziyuan", + label: "资源", + children: [ + { + value: "axure", + label: "Axure Components" + }, + { + value: "sketch", + label: "Sketch Templates" + }, + { + value: "jiaohu", + label: "组件交互文档" + } + ] + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +mock.push({ + path: "/select/cascadeData", + method: "get", + handle(req) { + return { + code: 0, + msg: "success", + data: dictData + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/group/api.js b/packages/ui/certd-client/src/views/crud/form/group/api.js new file mode 100644 index 000000000..b1fcf8560 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/group/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormGroup"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/group/crud.jsx b/packages/ui/certd-client/src/views/crud/form/group/crud.jsx new file mode 100644 index 000000000..8016d35e4 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/group/crud.jsx @@ -0,0 +1,143 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + title: { + title: "商品标题", + type: "text" + }, + code: { + title: "商品代码", + search: { show: true }, + type: "text" + }, + images: { + title: "图片", + type: "image-uploader" + }, + price: { + title: "价格", + sortable: true + }, + store: { + title: "库存", + type: "number" + }, + intro: { + title: "简介", + type: "textarea", + column: { + ellipsis: true, + showTitle: true + } + }, + content: { + title: "详情", + type: "editor-ueditor", + form: { + labelWidth: "0px" + } + }, + slotField: { + title: "插槽示例", + type: "text" + }, + product: { + title: "未分组字段", + type: "text", + form: { + col: { span: 24 }, + helper: "未分组的字段会显示在这里,一般来说你应该把所有字段都编入分组内" + } + }, + hidden1: { + title: "隐藏1", + type: "text", + form: { + show: false + }, + column: { + show: false + } + }, + hidden2: { + title: "隐藏2", + type: "text", + form: { + show: false + }, + column: { + show: false + } + } + }, + form: { + group: { + type: "collapse", // tab + accordion: true, //手风琴模式 + groups: { + base: { + slots: { + //自定义header + header: () => { + return ( + + 商品基础 + + + ); + } + }, + columns: ["code", "title", "images", "invalidKey"] + }, + price: { + header: "库存价格", + columns: ["store", "price"] + }, + info: { + header: "详情", + collapsed: true, //默认折叠 + columns: ["intro", "content", "slotField"] + }, + hiddenTest: { + header: "分组隐藏测试", //如果组里面的所有的组件都配置了隐藏,则本分组隐藏 + columns: ["hidden1", "hidden2"] + } + // custom: { + // title: "自定义", + // collapsed: false, + // show(context) { + // console.log("custom context", context); + // return context.mode === "view"; + // }, + // disabled: false, + // icon: "el-icon-warning-outline", + // columns: ["custom", "custom2"] + // } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/group/index.vue b/packages/ui/certd-client/src/views/crud/form/group/index.vue new file mode 100644 index 000000000..14e290c72 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/group/index.vue @@ -0,0 +1,46 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/group/mock.js b/packages/ui/certd-client/src/views/crud/form/group/mock.js new file mode 100644 index 000000000..70d8f4d4d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/group/mock.js @@ -0,0 +1,303 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormGroup", + idGenerator: 0 +}; +const list = [ + { + title: "无线充电宝", + code: "100001", + images: "https://img0.bdstatic.com/static/searchdetail/img/logo-2X_0c4ef02.png", + price: 100, + stock: 99, + intro: "30000毫安超大容量移动电源充电宝官方原装正品专用便携", + content: "" + } +]; + +const dictData = [ + { + value: "zhinan", + label: "指南", + children: [ + { + value: "shejiyuanze", + label: "设计原则", + children: [ + { + value: "yizhi", + label: "一致" + }, + { + value: "fankui", + label: "反馈" + }, + { + value: "xiaolv", + label: "效率" + }, + { + value: "kekong", + label: "可控" + } + ] + }, + { + value: "daohang", + label: "导航", + children: [ + { + value: "cexiangdaohang", + label: "侧向导航" + }, + { + value: "dingbudaohang", + label: "顶部导航" + } + ] + } + ] + }, + { + value: "zujian", + label: "组件", + children: [ + { + value: "basic", + label: "Basic", + children: [ + { + value: "layout", + label: "Layout 布局" + }, + { + value: "color", + label: "Color 色彩" + }, + { + value: "typography", + label: "Typography 字体" + }, + { + value: "icon", + label: "Icon 图标" + }, + { + value: "button", + label: "Button 按钮" + } + ] + }, + { + value: "form", + label: "Form", + children: [ + { + value: "radio", + label: "Radio 单选框" + }, + { + value: "checkbox", + label: "Checkbox 多选框" + }, + { + value: "input", + label: "Input 输入框" + }, + { + value: "input-number", + label: "InputNumber 计数器" + }, + { + value: "select", + label: "Select 选择器" + }, + { + value: "cascader", + label: "Cascader 级联选择器" + }, + { + value: "switch", + label: "Switch 开关" + }, + { + value: "slider", + label: "Slider 滑块" + }, + { + value: "time-picker", + label: "TimePicker 时间选择器" + }, + { + value: "date-picker", + label: "DatePicker 日期选择器" + }, + { + value: "datetime-picker", + label: "DateTimePicker 日期时间选择器" + }, + { + value: "upload", + label: "Upload 上传" + }, + { + value: "rate", + label: "Rate 评分" + }, + { + value: "form", + label: "Form 表单" + } + ] + }, + { + value: "data", + label: "Data", + children: [ + { + value: "table", + label: "Table 表格" + }, + { + value: "tag", + label: "Tag 标签" + }, + { + value: "progress", + label: "Progress 进度条" + }, + { + value: "tree", + label: "Tree 树形控件" + }, + { + value: "pagination", + label: "Pagination 分页" + }, + { + value: "badge", + label: "Badge 标记" + } + ] + }, + { + value: "notice", + label: "Notice", + children: [ + { + value: "alert", + label: "Alert 警告" + }, + { + value: "loading", + label: "Loading 加载" + }, + { + value: "message", + label: "Message 消息提示" + }, + { + value: "message-box", + label: "MessageBox 弹框" + }, + { + value: "notification", + label: "Notification 通知" + } + ] + }, + { + value: "navigation", + label: "Navigation", + children: [ + { + value: "menu", + label: "NavMenu 导航菜单" + }, + { + value: "tabs", + label: "Tabs 标签页" + }, + { + value: "breadcrumb", + label: "Breadcrumb 面包屑" + }, + { + value: "dropdown", + label: "Dropdown 下拉菜单" + }, + { + value: "steps", + label: "Steps 步骤条" + } + ] + }, + { + value: "others", + label: "Others", + children: [ + { + value: "dialog", + label: "Dialog 对话框" + }, + { + value: "tooltip", + label: "Tooltip 文字提示" + }, + { + value: "popover", + label: "Popover 弹出框" + }, + { + value: "card", + label: "Card 卡片" + }, + { + value: "carousel", + label: "Carousel 走马灯" + }, + { + value: "collapse", + label: "Collapse 折叠面板" + } + ] + } + ] + }, + { + value: "ziyuan", + label: "资源", + children: [ + { + value: "axure", + label: "Axure Components" + }, + { + value: "sketch", + label: "Sketch Templates" + }, + { + value: "jiaohu", + label: "组件交互文档" + } + ] + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +mock.push({ + path: "/select/cascadeData", + method: "get", + handle(req) { + return { + code: 0, + msg: "success", + data: dictData + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/helper/api.js b/packages/ui/certd-client/src/views/crud/form/helper/api.js new file mode 100644 index 000000000..aa8f08902 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/helper/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormHelper"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/helper/crud.jsx b/packages/ui/certd-client/src/views/crud/form/helper/crud.jsx new file mode 100644 index 000000000..e9946e84f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/helper/crud.jsx @@ -0,0 +1,72 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + labelCol: { span: 6 }, + wrapperCol: { span: 16 }, + helper: { + // position: "label" // helper的展示位置全局配置 + // tooltip:{} + } + }, + columns: { + name: { + title: "最简单", + type: "text", + form: { + helper: "最简单的helper\n换行测试" + } + }, + age: { + title: "jsx", + type: "text", + form: { + helper: { + render() { + return
jsx自定义render
; + } + } + } + }, + status: { + title: "显示在label", + type: "text", + form: { + rules: [{ required: true, message: "此项必填" }], + helper: { + position: "label", + tooltip: { + placement: "topLeft" + }, + text: "在label通过tooltip方式显示的helper\n换行测试" + // render() { + // return
在label通过tooltip方式显示的helper
; + // } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/helper/index.vue b/packages/ui/certd-client/src/views/crud/form/helper/index.vue new file mode 100644 index 000000000..440574488 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/helper/index.vue @@ -0,0 +1,48 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/helper/mock.js b/packages/ui/certd-client/src/views/crud/form/helper/mock.js new file mode 100644 index 000000000..92b3dacaa --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/helper/mock.js @@ -0,0 +1,310 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormHelper", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + age: 15, + password: "", + status: "2", + url: "https://baidu.com" + }, + { + name: "张三", + age: 18, + password: "", + url: "https://baidu.com" + }, + { + status: "2" + } +]; + +const dictData = [ + { + value: "zhinan", + label: "指南", + children: [ + { + value: "shejiyuanze", + label: "设计原则", + children: [ + { + value: "yizhi", + label: "一致" + }, + { + value: "fankui", + label: "反馈" + }, + { + value: "xiaolv", + label: "效率" + }, + { + value: "kekong", + label: "可控" + } + ] + }, + { + value: "daohang", + label: "导航", + children: [ + { + value: "cexiangdaohang", + label: "侧向导航" + }, + { + value: "dingbudaohang", + label: "顶部导航" + } + ] + } + ] + }, + { + value: "zujian", + label: "组件", + children: [ + { + value: "basic", + label: "Basic", + children: [ + { + value: "layout", + label: "Layout 布局" + }, + { + value: "color", + label: "Color 色彩" + }, + { + value: "typography", + label: "Typography 字体" + }, + { + value: "icon", + label: "Icon 图标" + }, + { + value: "button", + label: "Button 按钮" + } + ] + }, + { + value: "form", + label: "Form", + children: [ + { + value: "radio", + label: "Radio 单选框" + }, + { + value: "checkbox", + label: "Checkbox 多选框" + }, + { + value: "input", + label: "Input 输入框" + }, + { + value: "input-number", + label: "InputNumber 计数器" + }, + { + value: "select", + label: "Select 选择器" + }, + { + value: "cascader", + label: "Cascader 级联选择器" + }, + { + value: "switch", + label: "Switch 开关" + }, + { + value: "slider", + label: "Slider 滑块" + }, + { + value: "time-picker", + label: "TimePicker 时间选择器" + }, + { + value: "date-picker", + label: "DatePicker 日期选择器" + }, + { + value: "datetime-picker", + label: "DateTimePicker 日期时间选择器" + }, + { + value: "upload", + label: "Upload 上传" + }, + { + value: "rate", + label: "Rate 评分" + }, + { + value: "form", + label: "Form 表单" + } + ] + }, + { + value: "data", + label: "Data", + children: [ + { + value: "table", + label: "Table 表格" + }, + { + value: "tag", + label: "Tag 标签" + }, + { + value: "progress", + label: "Progress 进度条" + }, + { + value: "tree", + label: "Tree 树形控件" + }, + { + value: "pagination", + label: "Pagination 分页" + }, + { + value: "badge", + label: "Badge 标记" + } + ] + }, + { + value: "notice", + label: "Notice", + children: [ + { + value: "alert", + label: "Alert 警告" + }, + { + value: "loading", + label: "Loading 加载" + }, + { + value: "message", + label: "Message 消息提示" + }, + { + value: "message-box", + label: "MessageBox 弹框" + }, + { + value: "notification", + label: "Notification 通知" + } + ] + }, + { + value: "navigation", + label: "Navigation", + children: [ + { + value: "menu", + label: "NavMenu 导航菜单" + }, + { + value: "tabs", + label: "Tabs 标签页" + }, + { + value: "breadcrumb", + label: "Breadcrumb 面包屑" + }, + { + value: "dropdown", + label: "Dropdown 下拉菜单" + }, + { + value: "steps", + label: "Steps 步骤条" + } + ] + }, + { + value: "others", + label: "Others", + children: [ + { + value: "dialog", + label: "Dialog 对话框" + }, + { + value: "tooltip", + label: "Tooltip 文字提示" + }, + { + value: "popover", + label: "Popover 弹出框" + }, + { + value: "card", + label: "Card 卡片" + }, + { + value: "carousel", + label: "Carousel 走马灯" + }, + { + value: "collapse", + label: "Collapse 折叠面板" + } + ] + } + ] + }, + { + value: "ziyuan", + label: "资源", + children: [ + { + value: "axure", + label: "Axure Components" + }, + { + value: "sketch", + label: "Sketch Templates" + }, + { + value: "jiaohu", + label: "组件交互文档" + } + ] + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +mock.push({ + path: "/select/cascadeData", + method: "get", + handle(req) { + return { + code: 0, + msg: "success", + data: dictData + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/independent/crud.jsx b/packages/ui/certd-client/src/views/crud/form/independent/crud.jsx new file mode 100644 index 000000000..7e492d640 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/independent/crud.jsx @@ -0,0 +1,59 @@ +export default function ({ expose }) { + return { + crudOptions: { + form: { + wrapper: { + onClosed(e) { + console.log("onClosed", e); + }, + onOpened(e) { + console.log("onOpened", e); + } + }, + labelCol: { span: 6 }, + wrapperCol: { span: 16 }, + helper: { + // position: "label" // helper的展示位置全局配置 + // tooltip:{} + } + }, + columns: { + name: { + title: "最简单", + type: "text", + form: { + helper: "最简单的helper" + } + }, + age: { + title: "jsx", + type: "text", + form: { + helper: { + render() { + return
jsx自定义render
; + } + } + } + }, + status: { + title: "显示在label", + type: "text", + form: { + rules: [{ required: true, message: "此项必填" }], + helper: { + position: "label", + tooltip: { + placement: "topLeft" + }, + text: "在label通过tooltip方式显示的helper" + // render() { + // return
在label通过tooltip方式显示的helper
; + // } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/independent/index.vue b/packages/ui/certd-client/src/views/crud/form/independent/index.vue new file mode 100644 index 000000000..633904440 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/independent/index.vue @@ -0,0 +1,215 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/inner/api.js b/packages/ui/certd-client/src/views/crud/form/inner/api.js new file mode 100644 index 000000000..d8c3543c8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/inner/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormInner"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/inner/area/api.js b/packages/ui/certd-client/src/views/crud/form/inner/area/api.js new file mode 100644 index 000000000..b9142c890 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/inner/area/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormInnerArea"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function GetAll(query) { + return request({ + url: apiPrefix + "/all", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/inner/area/crud.jsx b/packages/ui/certd-client/src/views/crud/form/inner/area/crud.jsx new file mode 100644 index 000000000..dbd13b803 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/inner/area/crud.jsx @@ -0,0 +1,39 @@ +import * as api from "./api"; +import { dict, useExpose } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + wrapper: { + inner: true + } + }, + columns: { + area: { + title: "地区", + type: "text" + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/inner/area/index.vue b/packages/ui/certd-client/src/views/crud/form/inner/area/index.vue new file mode 100644 index 000000000..ab81c09b0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/inner/area/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/inner/area/mock.js b/packages/ui/certd-client/src/views/crud/form/inner/area/mock.js new file mode 100644 index 000000000..d7c076ef2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/inner/area/mock.js @@ -0,0 +1,24 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormInnerArea", + idGenerator: 0 +}; +const list = [ + { + area: "深圳" + }, + { + area: "北京" + }, + { + area: "上海" + }, + { + area: "广州" + } +]; +options.list = list; +options.copyTimes = 1; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/inner/crud.jsx b/packages/ui/certd-client/src/views/crud/form/inner/crud.jsx new file mode 100644 index 000000000..c7009bf7f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/inner/crud.jsx @@ -0,0 +1,77 @@ +import * as api from "./api"; +import { dict, useExpose } from "@fast-crud/fast-crud"; +import { useRouter } from "vue-router"; +import { message } from "ant-design-vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + const router = useRouter(); + const areaDict = dict({ + value: "id", + label: "area", + url: "/mock/FormInnerArea/all" + }); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + wrapper: { + inner: true + } + }, + columns: { + name: { + title: "姓名", + type: "text" + }, + age: { + title: "年龄", + type: "text" + }, + area: { + title: "地区", + type: "dict-select", + dict: areaDict, + form: { + suffixRender() { + function refresh() { + message.info("刷新dict"); + areaDict.reloadDict(); + } + function gotoAddArea() { + message.info("调用 router.push 打开地区管理页面"); + router.push({ path: "/crud/form/inner/area" }); + } + return ( + + + + + 添加地区 + + ); + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/inner/index.vue b/packages/ui/certd-client/src/views/crud/form/inner/index.vue new file mode 100644 index 000000000..ab81c09b0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/inner/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/inner/mock.js b/packages/ui/certd-client/src/views/crud/form/inner/mock.js new file mode 100644 index 000000000..a9f809c81 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/inner/mock.js @@ -0,0 +1,25 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormInner", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + age: 15, + password: "", + status: "2", + url: "https://baidu.com" + }, + { + name: "张三", + age: 18, + password: "", + url: "https://baidu.com" + } +]; +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/layout-flex/api.js b/packages/ui/certd-client/src/views/crud/form/layout-flex/api.js new file mode 100644 index 000000000..0ba39afe2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout-flex/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/formLayoutFlex"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/layout-flex/crud.jsx b/packages/ui/certd-client/src/views/crud/form/layout-flex/crud.jsx new file mode 100644 index 000000000..1e7c5c383 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout-flex/crud.jsx @@ -0,0 +1,63 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + display: "flex", + labelCol: { + //固定label宽度 + span: null, + style: { + width: "120px" + } + }, + wrapperCol: { + span: null + } + }, + columns: { + name: { + title: "姓名", + type: "text", + search: { show: true } + }, + order: { + title: "字段排序", + type: "text", + form: { + order: 0 + } + }, + intro: { + title: "跨列", + search: { show: true }, + type: ["textarea"], + form: { + col: { span: 24 } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/layout-flex/index.vue b/packages/ui/certd-client/src/views/crud/form/layout-flex/index.vue new file mode 100644 index 000000000..e43bc8cf1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout-flex/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/layout-flex/mock.js b/packages/ui/certd-client/src/views/crud/form/layout-flex/mock.js new file mode 100644 index 000000000..e48c1f0fd --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout-flex/mock.js @@ -0,0 +1,35 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "formLayoutFlex", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + date: "2016-05-02", + status: "0", + province: "1", + avatar: "https://alicdn.antdv.com/vue.png", + show: true, + city: "sz", + address: "123123", + zip: "518000", + order: "我在编辑的时候会排到第一个", + intro: "王小虎是element-plus的table示例出现的名字" + }, + { + name: "张三", + date: "2016-05-04", + status: "1", + province: "2" + }, + { + name: "李四", + date: 2232433534511, + status: "1", + province: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/layout-grid/api.js b/packages/ui/certd-client/src/views/crud/form/layout-grid/api.js new file mode 100644 index 000000000..c00a57873 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout-grid/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/formLayoutGrid"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/layout-grid/crud.jsx b/packages/ui/certd-client/src/views/crud/form/layout-grid/crud.jsx new file mode 100644 index 000000000..284f7b9f8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout-grid/crud.jsx @@ -0,0 +1,67 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + // 具体可配置请参考 grid 布局: http://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html + display: "grid" + }, + columns: { + avatar: { + title: "头像上传", + type: "avatar-uploader", + form: { + order: 0, + col: { + style: { gridRow: "span 3" } + }, + helper: "通过grid布局,可以实现比flex更加规整的排列" + } + }, + name: { + title: "姓名", + type: "text", + search: { show: true } + }, + order: { + title: "占位演示", + type: "text" + }, + place: { + title: "占位演示", + type: "text" + }, + intro: { + title: "跨列", + type: "textarea", + form: { + col: { + style: { gridColumn: "span 2" } // grid 模式控制跨列 + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/layout-grid/index.vue b/packages/ui/certd-client/src/views/crud/form/layout-grid/index.vue new file mode 100644 index 000000000..499e5fe7d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout-grid/index.vue @@ -0,0 +1,52 @@ + + + + diff --git a/packages/ui/certd-client/src/views/crud/form/layout-grid/mock.js b/packages/ui/certd-client/src/views/crud/form/layout-grid/mock.js new file mode 100644 index 000000000..41ec78880 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout-grid/mock.js @@ -0,0 +1,34 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "formLayoutGrid", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + date: "2016-05-02", + status: "0", + province: "1", + avatar: "https://alicdn.antdv.com/vue.png", + show: true, + city: "sz", + address: "123123", + zip: "518000", + order: "我在编辑的时候会排到第一个", + intro: "王小虎是element-plus的table示例出现的名字" + }, + { + name: "张三", + date: "2016-05-04", + status: "1" + }, + { + name: "李四", + date: 2232433534511, + status: "1", + province: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/layout/api.js b/packages/ui/certd-client/src/views/crud/form/layout/api.js new file mode 100644 index 000000000..d9a789418 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/formLayout"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/layout/crud.jsx b/packages/ui/certd-client/src/views/crud/form/layout/crud.jsx new file mode 100644 index 000000000..fabaec8ee --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout/crud.jsx @@ -0,0 +1,101 @@ +import * as api from "./api"; +import { dict, compute } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + + const { getFormData, getFormWrapperRef } = expose; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + /** + * flex模式,通过 + * grid模式 + */ + display: "flex", + wrapper: { + customClass: "page-layout", + onOpened(context) { + getFormWrapperRef().formOptions.display = context.options.initial?.display; + console.log("form opened", context, getFormData()); + } + } + }, + columns: { + display: { + title: "布局", + type: "dict-radio", + dict: dict({ + data: [ + { value: "flex", label: "flex", color: "blue" }, + { value: "grid", label: "grid", color: "green" } + ] + }), + search: { show: true, valueChange: null }, + form: { + valueChange(context) { + const { value } = context; + getFormWrapperRef().formOptions.display = value; + console.log("valueChange", value, context); + } + } + }, + name: { + title: "姓名", + type: "text", + search: { show: true } + }, + zip: { + title: "邮编", + type: "text" + }, + blank: { + title: "表单占位栏", + type: "text", + form: { + blank: true + } + }, + gridSpan: { + title: "grid跨列", + type: "textarea", + form: { + col: { + style: { gridColumn: "span 2" } // grid 模式 + } + } + }, + flexSpan: { + title: "flex跨列", + type: "textarea", + search: { show: false }, + form: { + show: compute((context) => { + // grid跨列模式下使用flex模式的设置会显示异常,为了演示效果,在grid模式下隐藏 + return context.form.display !== "grid"; + }), + col: { span: 24 } // flex模式跨列配置 + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/layout/index.vue b/packages/ui/certd-client/src/views/crud/form/layout/index.vue new file mode 100644 index 000000000..1a969b414 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout/index.vue @@ -0,0 +1,50 @@ + + + + diff --git a/packages/ui/certd-client/src/views/crud/form/layout/mock.js b/packages/ui/certd-client/src/views/crud/form/layout/mock.js new file mode 100644 index 000000000..f63208a5a --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/layout/mock.js @@ -0,0 +1,41 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "formLayout", + idGenerator: 0 +}; +const list = [ + { + display: "flex", + name: "aa", + date: "2016-05-02", + status: "0", + province: "1", + avatar: "https://alicdn.antdv.com/vue.png", + show: true, + city: "sz", + address: "123123", + zip: "518000" + }, + { + display: "grid", + name: "bb", + date: "2016-05-04", + status: "1", + province: "2" + }, + { + name: "cc", + date: 2232433534511, + status: "1", + province: "0" + }, + { + name: "dd", + date: "2016-05-03", + status: "2", + province: "wh,gz" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/nest/api.js b/packages/ui/certd-client/src/views/crud/form/nest/api.js new file mode 100644 index 000000000..f7d01faa7 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/nest/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormNestObject"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/nest/crud.jsx b/packages/ui/certd-client/src/views/crud/form/nest/crud.jsx new file mode 100644 index 000000000..1bf0a55af --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/nest/crud.jsx @@ -0,0 +1,76 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + labelCol: { span: 6 }, + wrapperCol: { span: 16 }, + helper: { + // position: "label" // helper的展示位置全局配置 + // tooltip:{} + } + }, + columns: { + username: { + title: "用户名", + type: "text" + }, + "profile.name": { + title: "profile.name", + type: "text", + search:{show:true}, + form: { + key: ["profile", "name"], + rules: [{ required: true, message: "姓名必填" }] + } + }, + "profile.age": { + title: "profile.age", + type: "number", + form: { + key: ["profile", "age"] + } + }, + "profile.status": { + title: "profile.status", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }), + form: { + key: ["profile", "status"] + } + }, + "profile.count": { + title: "不提交的字段", + type: "text", + form: { + submit: false, + key: ["profile", "count"] + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/nest/index.vue b/packages/ui/certd-client/src/views/crud/form/nest/index.vue new file mode 100644 index 000000000..5e4c48c99 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/nest/index.vue @@ -0,0 +1,48 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/nest/mock.js b/packages/ui/certd-client/src/views/crud/form/nest/mock.js new file mode 100644 index 000000000..1658d74a1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/nest/mock.js @@ -0,0 +1,319 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormNestObject", + idGenerator: 0 +}; +const list = [ + { + username: "wangxiaohu", + profile: { + name: "王小虎", + age: 15, + status: "1", + count: "配置submit=false将不会提交给后台" + } + }, + { + username: "zhangsan", + profile: { + name: "张三", + age: 18, + status: "2" + } + }, + { + username: "lisi", + profile: { + name: "李四", + age: 18, + status: "2" + } + } +]; + +const dictData = [ + { + value: "zhinan", + label: "指南", + children: [ + { + value: "shejiyuanze", + label: "设计原则", + children: [ + { + value: "yizhi", + label: "一致" + }, + { + value: "fankui", + label: "反馈" + }, + { + value: "xiaolv", + label: "效率" + }, + { + value: "kekong", + label: "可控" + } + ] + }, + { + value: "daohang", + label: "导航", + children: [ + { + value: "cexiangdaohang", + label: "侧向导航" + }, + { + value: "dingbudaohang", + label: "顶部导航" + } + ] + } + ] + }, + { + value: "zujian", + label: "组件", + children: [ + { + value: "basic", + label: "Basic", + children: [ + { + value: "layout", + label: "Layout 布局" + }, + { + value: "color", + label: "Color 色彩" + }, + { + value: "typography", + label: "Typography 字体" + }, + { + value: "icon", + label: "Icon 图标" + }, + { + value: "button", + label: "Button 按钮" + } + ] + }, + { + value: "form", + label: "Form", + children: [ + { + value: "radio", + label: "Radio 单选框" + }, + { + value: "checkbox", + label: "Checkbox 多选框" + }, + { + value: "input", + label: "Input 输入框" + }, + { + value: "input-number", + label: "InputNumber 计数器" + }, + { + value: "select", + label: "Select 选择器" + }, + { + value: "cascader", + label: "Cascader 级联选择器" + }, + { + value: "switch", + label: "Switch 开关" + }, + { + value: "slider", + label: "Slider 滑块" + }, + { + value: "time-picker", + label: "TimePicker 时间选择器" + }, + { + value: "date-picker", + label: "DatePicker 日期选择器" + }, + { + value: "datetime-picker", + label: "DateTimePicker 日期时间选择器" + }, + { + value: "upload", + label: "Upload 上传" + }, + { + value: "rate", + label: "Rate 评分" + }, + { + value: "form", + label: "Form 表单" + } + ] + }, + { + value: "data", + label: "Data", + children: [ + { + value: "table", + label: "Table 表格" + }, + { + value: "tag", + label: "Tag 标签" + }, + { + value: "progress", + label: "Progress 进度条" + }, + { + value: "tree", + label: "Tree 树形控件" + }, + { + value: "pagination", + label: "Pagination 分页" + }, + { + value: "badge", + label: "Badge 标记" + } + ] + }, + { + value: "notice", + label: "Notice", + children: [ + { + value: "alert", + label: "Alert 警告" + }, + { + value: "loading", + label: "Loading 加载" + }, + { + value: "message", + label: "Message 消息提示" + }, + { + value: "message-box", + label: "MessageBox 弹框" + }, + { + value: "notification", + label: "Notification 通知" + } + ] + }, + { + value: "navigation", + label: "Navigation", + children: [ + { + value: "menu", + label: "NavMenu 导航菜单" + }, + { + value: "tabs", + label: "Tabs 标签页" + }, + { + value: "breadcrumb", + label: "Breadcrumb 面包屑" + }, + { + value: "dropdown", + label: "Dropdown 下拉菜单" + }, + { + value: "steps", + label: "Steps 步骤条" + } + ] + }, + { + value: "others", + label: "Others", + children: [ + { + value: "dialog", + label: "Dialog 对话框" + }, + { + value: "tooltip", + label: "Tooltip 文字提示" + }, + { + value: "popover", + label: "Popover 弹出框" + }, + { + value: "card", + label: "Card 卡片" + }, + { + value: "carousel", + label: "Carousel 走马灯" + }, + { + value: "collapse", + label: "Collapse 折叠面板" + } + ] + } + ] + }, + { + value: "ziyuan", + label: "资源", + children: [ + { + value: "axure", + label: "Axure Components" + }, + { + value: "sketch", + label: "Sketch Templates" + }, + { + value: "jiaohu", + label: "组件交互文档" + } + ] + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +mock.push({ + path: "/select/cascadeData", + method: "get", + handle(req) { + return { + code: 0, + msg: "success", + data: dictData + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/new-page/api.js b/packages/ui/certd-client/src/views/crud/form/new-page/api.js new file mode 100644 index 000000000..0dc2616a0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/new-page/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormNewPage"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/get", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/new-page/crud.jsx b/packages/ui/certd-client/src/views/crud/form/new-page/crud.jsx new file mode 100644 index 000000000..c68282819 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/new-page/crud.jsx @@ -0,0 +1,96 @@ +import * as api from "./api"; +import { useRouter } from "vue-router"; +export default function ({ expose }) { + const router = useRouter(); + + const { getFormRef, getFormData } = expose; + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + if (row.id) { + form.id = row.id; + } + + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + actionbar: { + buttons: { + add: { + click() { + router.push("/crud/form/new-page/edit"); + } + } + } + }, + rowHandle: { + buttons: { + edit: { + click(context) { + router.push("/crud/form/new-page/edit?id=" + context.row.id); + } + } + } + }, + columns: { + title: { + title: "商品标题", + type: "text" + }, + code: { + title: "商品代码", + search: { show: true }, + type: "text" + }, + images: { + title: "图片", + type: "image-uploader" + }, + price: { + title: "价格", + sortable: true + }, + store: { + title: "库存", + type: "number" + }, + intro: { + title: "简介", + type: "textarea", + column: { + ellipsis: true + } + }, + content: { + title: "详情", + type: ["editor-wang", "colspan"], + form: { + itemProps: { labelWidth: "0px" } + } + }, + product: { + title: "未分组字段", + type: ["text", "colspan"], + form: { + helper: "未分组的字段会显示在这里,一般来说你应该把所有字段都编入分组内" + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/new-page/edit.vue b/packages/ui/certd-client/src/views/crud/form/new-page/edit.vue new file mode 100644 index 000000000..c93ef0bdd --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/new-page/edit.vue @@ -0,0 +1,81 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/new-page/index.vue b/packages/ui/certd-client/src/views/crud/form/new-page/index.vue new file mode 100644 index 000000000..0702f08c9 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/new-page/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/new-page/mock.js b/packages/ui/certd-client/src/views/crud/form/new-page/mock.js new file mode 100644 index 000000000..a182214c8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/new-page/mock.js @@ -0,0 +1,22 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormNewPage", + idGenerator: 0 +}; +const list = [ + { + title: "无线充电宝", + code: "100001", + images: "https://img0.bdstatic.com/static/searchdetail/img/logo-2X_0c4ef02.png", + price: 100, + stock: 99, + intro: "30000毫安超大容量移动电源充电宝官方原装正品专用便携", + content: "" + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/reset/api.js b/packages/ui/certd-client/src/views/crud/form/reset/api.js new file mode 100644 index 000000000..52a5ff5b0 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/reset/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/formReset"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/reset/crud.jsx b/packages/ui/certd-client/src/views/crud/form/reset/crud.jsx new file mode 100644 index 000000000..1a4f1df2f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/reset/crud.jsx @@ -0,0 +1,56 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + async doReset() { + console.log("reset之后可以执行其他操作"); + }, + wrapper: { + buttons: { + reset: { + text: "重置", + order: -1, + click(value) { + console.log("on reset", value); + expose.getFormRef().reset(); + } + } + } + } + }, + columns: { + name: { + title: "姓名", + type: "text", //虽然不写也能正确显示组件,但不建议省略它 + search: { show: true }, + form: { + component: { + maxlength: 20 + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/reset/index.vue b/packages/ui/certd-client/src/views/crud/form/reset/index.vue new file mode 100644 index 000000000..8c2e77e6d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/reset/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/reset/mock.js b/packages/ui/certd-client/src/views/crud/form/reset/mock.js new file mode 100644 index 000000000..f586e9c46 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/reset/mock.js @@ -0,0 +1,40 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "formReset", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + date: "2016-05-02", + status: "0", + province: "1", + avatar: "https://alicdn.antdv.com/vue.png", + show: true, + city: "sz", + address: "123123", + zip: "518000", + intro: "王小虎是element-plus的table示例出现的名字" + }, + { + name: "张三", + date: "2016-05-04", + status: "1", + province: "2" + }, + { + name: "李四", + date: 2232433534511, + status: "1", + province: "0" + }, + { + name: "王五", + date: "2016-05-03", + status: "2", + province: "wh,gz" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/form/validation/api.js b/packages/ui/certd-client/src/views/crud/form/validation/api.js new file mode 100644 index 000000000..f62ba48b1 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/validation/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/FormValidation"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/form/validation/crud.jsx b/packages/ui/certd-client/src/views/crud/form/validation/crud.jsx new file mode 100644 index 000000000..856ca779b --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/validation/crud.jsx @@ -0,0 +1,135 @@ +import * as api from "./api"; +import { dict, useExpose } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const { getFormRef, getFormData } = expose; + const validatePass1 = async (rule, value) => { + if (value === "") { + throw new Error("请输入密码"); + } + if (getFormData().password2 !== "") { + getFormRef().getFormRef().validateFields(["password2"]); + } + }; + const validatePass2 = async (rule, value) => { + if (value === "") { + throw new Error("请再次输入密码"); + } else if (value !== getFormData().password) { + throw new Error("两次输入密码不一致!"); + } + }; + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + row:{ + gutter:20 + }, + beforeSubmit(context) { + console.log("beforeSubmit", context); + }, + afterSubmit(context) { + console.log("afterSubmit", context); + } + }, + columns: { + name: { + title: "姓名", + type: "text", + form: { + helper: "添加和编辑时必填,编辑时额外需要校验长度", + rules: [{ required: true, message: "请输入姓名" }], + component: { + maxlength: 5, // 原生属性要写在这里 + props: { + type: "text", + showWordLimit: true + } + } + }, + editForm: { + rules: [{ min: 2, max: 5, message: "姓名长度为2-5" }] + } + }, + age: { + title: "年龄", + type: "text", + form: { + rules: [{ pattern: /^\d+$/, message: "必须为整数" }], + helper: "正则表达式" + } + }, + password: { + title: "密码", + type: "password", + column: { + component: { + cellRender() { + return ******; + } + } + }, + form: { + rules: [ + { required: true, message: "请输入密码" }, + { validator: validatePass1, trigger: "blur" } + ] + } + }, + password2: { + title: "确认密码", + type: "password", + column: { show: false }, + form: { + rules: [ + { required: true, message: "请输入确认密码" }, + { validator: validatePass2, trigger: "blur" } + ] + } + }, + status: { + title: "必选", + type: "dict-select", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum" + }), + form: { + rules: [{ required: true, message: "请选择一个选项" }] + } + }, + email: { + title: "邮箱", + type: "text", + form: { + rules: [{ type: "email", message: "请填写正确的邮箱" }] + } + }, + url: { + title: "URL", + type: "text", + form: { + rules: [{ type: "url", message: "请填写正确的url" }] + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/form/validation/index.vue b/packages/ui/certd-client/src/views/crud/form/validation/index.vue new file mode 100644 index 000000000..49bd373a8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/validation/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/form/validation/mock.js b/packages/ui/certd-client/src/views/crud/form/validation/mock.js new file mode 100644 index 000000000..0ca7bc117 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/form/validation/mock.js @@ -0,0 +1,310 @@ +import mockUtil from "/src/mock/base"; + +const options = { + name: "FormValidation", + idGenerator: 0 +}; +const list = [ + { + name: "王小虎", + age: 15, + password: "", + status: "2", + url: "https://baidu.com" + }, + { + name: "张三", + age: 18, + password: "", + url: "https://baidu.com" + }, + { + status: "2" + } +]; + +const dictData = [ + { + value: "zhinan", + label: "指南", + children: [ + { + value: "shejiyuanze", + label: "设计原则", + children: [ + { + value: "yizhi", + label: "一致" + }, + { + value: "fankui", + label: "反馈" + }, + { + value: "xiaolv", + label: "效率" + }, + { + value: "kekong", + label: "可控" + } + ] + }, + { + value: "daohang", + label: "导航", + children: [ + { + value: "cexiangdaohang", + label: "侧向导航" + }, + { + value: "dingbudaohang", + label: "顶部导航" + } + ] + } + ] + }, + { + value: "zujian", + label: "组件", + children: [ + { + value: "basic", + label: "Basic", + children: [ + { + value: "layout", + label: "Layout 布局" + }, + { + value: "color", + label: "Color 色彩" + }, + { + value: "typography", + label: "Typography 字体" + }, + { + value: "icon", + label: "Icon 图标" + }, + { + value: "button", + label: "Button 按钮" + } + ] + }, + { + value: "form", + label: "Form", + children: [ + { + value: "radio", + label: "Radio 单选框" + }, + { + value: "checkbox", + label: "Checkbox 多选框" + }, + { + value: "input", + label: "Input 输入框" + }, + { + value: "input-number", + label: "InputNumber 计数器" + }, + { + value: "select", + label: "Select 选择器" + }, + { + value: "cascader", + label: "Cascader 级联选择器" + }, + { + value: "switch", + label: "Switch 开关" + }, + { + value: "slider", + label: "Slider 滑块" + }, + { + value: "time-picker", + label: "TimePicker 时间选择器" + }, + { + value: "date-picker", + label: "DatePicker 日期选择器" + }, + { + value: "datetime-picker", + label: "DateTimePicker 日期时间选择器" + }, + { + value: "upload", + label: "Upload 上传" + }, + { + value: "rate", + label: "Rate 评分" + }, + { + value: "form", + label: "Form 表单" + } + ] + }, + { + value: "data", + label: "Data", + children: [ + { + value: "table", + label: "Table 表格" + }, + { + value: "tag", + label: "Tag 标签" + }, + { + value: "progress", + label: "Progress 进度条" + }, + { + value: "tree", + label: "Tree 树形控件" + }, + { + value: "pagination", + label: "Pagination 分页" + }, + { + value: "badge", + label: "Badge 标记" + } + ] + }, + { + value: "notice", + label: "Notice", + children: [ + { + value: "alert", + label: "Alert 警告" + }, + { + value: "loading", + label: "Loading 加载" + }, + { + value: "message", + label: "Message 消息提示" + }, + { + value: "message-box", + label: "MessageBox 弹框" + }, + { + value: "notification", + label: "Notification 通知" + } + ] + }, + { + value: "navigation", + label: "Navigation", + children: [ + { + value: "menu", + label: "NavMenu 导航菜单" + }, + { + value: "tabs", + label: "Tabs 标签页" + }, + { + value: "breadcrumb", + label: "Breadcrumb 面包屑" + }, + { + value: "dropdown", + label: "Dropdown 下拉菜单" + }, + { + value: "steps", + label: "Steps 步骤条" + } + ] + }, + { + value: "others", + label: "Others", + children: [ + { + value: "dialog", + label: "Dialog 对话框" + }, + { + value: "tooltip", + label: "Tooltip 文字提示" + }, + { + value: "popover", + label: "Popover 弹出框" + }, + { + value: "card", + label: "Card 卡片" + }, + { + value: "carousel", + label: "Carousel 走马灯" + }, + { + value: "collapse", + label: "Collapse 折叠面板" + } + ] + } + ] + }, + { + value: "ziyuan", + label: "资源", + children: [ + { + value: "axure", + label: "Axure Components" + }, + { + value: "sketch", + label: "Sketch Templates" + }, + { + value: "jiaohu", + label: "组件交互文档" + } + ] + } +]; + +options.list = list; +options.copyTimes = 1000; +const mock = mockUtil.buildMock(options); +mock.push({ + path: "/select/cascadeData", + method: "get", + handle(req) { + return { + code: 0, + msg: "success", + data: dictData + }; + } +}); + +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/home/index.vue b/packages/ui/certd-client/src/views/crud/home/index.vue new file mode 100644 index 000000000..274457799 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/home/index.vue @@ -0,0 +1,15 @@ + + + + diff --git a/packages/ui/certd-client/src/views/crud/home/page-cover/helper.js b/packages/ui/certd-client/src/views/crud/home/page-cover/helper.js new file mode 100644 index 000000000..5c955ba6e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/home/page-cover/helper.js @@ -0,0 +1,31 @@ +export default { + crud: ` columns: [ + date:{ + title: '日期', //字段名称 + type: 'date', //字段类型,添加、修改、查询将自动生成相应表单组件 + }, + status: { + title: '状态', + type: 'dict-select', //选择框,默认单选 + dict: dict({ url: '/dicts/OpenStatusEnum' })//远程数据字典 + }, + province: { + title: '地区', + type: 'dict-select', //选择框 + form: { //表单组件自定义配置,此处配置选择框为多选 + component: { //支持任何v-model组件 + filterable: true, multiple: true, clearable: true + } + }, + dict: dict({ + data: [ //本地数据字典 + { value: 'sz', label: '深圳' }, + { value: 'gz', label: '广州' }, + { value: 'wh', label: '武汉' }, + { value: 'sh', label: '上海' } + ] + }) + } + ] + ` +}; diff --git a/packages/ui/certd-client/src/views/crud/home/page-cover/image/crud.png b/packages/ui/certd-client/src/views/crud/home/page-cover/image/crud.png new file mode 100644 index 000000000..3dfb047dd Binary files /dev/null and b/packages/ui/certd-client/src/views/crud/home/page-cover/image/crud.png differ diff --git a/packages/ui/certd-client/src/views/crud/home/page-cover/image/darkblue@2x.png b/packages/ui/certd-client/src/views/crud/home/page-cover/image/darkblue@2x.png new file mode 100644 index 000000000..efe1577f4 Binary files /dev/null and b/packages/ui/certd-client/src/views/crud/home/page-cover/image/darkblue@2x.png differ diff --git a/packages/ui/certd-client/src/views/crud/home/page-cover/image/logo.svg b/packages/ui/certd-client/src/views/crud/home/page-cover/image/logo.svg new file mode 100644 index 000000000..7a33425e3 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/home/page-cover/image/logo.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/certd-client/src/views/crud/home/page-cover/index.vue b/packages/ui/certd-client/src/views/crud/home/page-cover/index.vue new file mode 100644 index 000000000..7c51dea3d --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/home/page-cover/index.vue @@ -0,0 +1,139 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/row-handle/dropdown/api.js b/packages/ui/certd-client/src/views/crud/row-handle/dropdown/api.js new file mode 100644 index 000000000..3cfd81822 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/row-handle/dropdown/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/RowHandleDropdown"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/row-handle/dropdown/crud.jsx b/packages/ui/certd-client/src/views/crud/row-handle/dropdown/crud.jsx new file mode 100644 index 000000000..2947b5377 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/row-handle/dropdown/crud.jsx @@ -0,0 +1,82 @@ +import * as api from "./api"; +import { dict, compute } from "@fast-crud/fast-crud"; +import { message } from "ant-design-vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + width: 290, + buttons: { + remove: { + // 根据row的值判断按钮是否显示 + show: compute(({ row }) => { + return row.radio !== "0"; + }), + dropdown: true //---------》给想要折叠的按钮配置dropdown为true,就会放入dropdown中《--------------- + }, + orderExample: { + text: "我排前面", + title: "按钮排序示例", + type: "link", + order: 0, //数字越小,越靠前,默认排序号为1 + click(opts) { + console.log("自定义操作列按钮点击", opts); + message.success("自定义操作列按钮点击"); + } + } + }, + dropdown: { + // 操作列折叠 + // 至少几个以上的按钮才会被折叠 + // atLeast: 2, //TODO 注意 [atLeast]参数即将废弃,请给button配置dropdown即可放入折叠 + more: { + text: "更多", + icon: null, + iconRight: "ion:caret-down-outline" + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/row-handle/dropdown/index.vue b/packages/ui/certd-client/src/views/crud/row-handle/dropdown/index.vue new file mode 100644 index 000000000..45d6937e2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/row-handle/dropdown/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/row-handle/dropdown/mock.js b/packages/ui/certd-client/src/views/crud/row-handle/dropdown/mock.js new file mode 100644 index 000000000..ed703ceb8 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/row-handle/dropdown/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "RowHandleDropdown", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/row-handle/tooltip/api.js b/packages/ui/certd-client/src/views/crud/row-handle/tooltip/api.js new file mode 100644 index 000000000..dd8bf7d8c --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/row-handle/tooltip/api.js @@ -0,0 +1,50 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/RowHandleTooltip"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} + +export function BatchDelete(ids) { + return request({ + url: apiPrefix + "/batchDelete", + method: "post", + data: { ids } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/row-handle/tooltip/crud.jsx b/packages/ui/certd-client/src/views/crud/row-handle/tooltip/crud.jsx new file mode 100644 index 000000000..b8cc0141e --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/row-handle/tooltip/crud.jsx @@ -0,0 +1,84 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import { ref } from "vue"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + width: 400, + buttons: { + edit: { + tooltip: { + title: "编辑" + } + }, + view: { + tooltip: { + title: "查看" + } + }, + remove: { + tooltip: { + title: "删除" + } + }, + custom: { + text: "tooltip title render", + tooltip: { + slots: { + title() { + return ( +
+ 我是自定义render +
+ ); + } + } + } + } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/row-handle/tooltip/index.vue b/packages/ui/certd-client/src/views/crud/row-handle/tooltip/index.vue new file mode 100644 index 000000000..40dde985f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/row-handle/tooltip/index.vue @@ -0,0 +1,51 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/row-handle/tooltip/mock.js b/packages/ui/certd-client/src/views/crud/row-handle/tooltip/mock.js new file mode 100644 index 000000000..7efa34b73 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/row-handle/tooltip/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "RowHandleTooltip", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/slots/cell/api.js b/packages/ui/certd-client/src/views/crud/slots/cell/api.js new file mode 100644 index 000000000..69094b0dd --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/cell/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/SlotsCell"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/slots/cell/crud.jsx b/packages/ui/certd-client/src/views/crud/slots/cell/crud.jsx new file mode 100644 index 000000000..ad836c9b2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/cell/crud.jsx @@ -0,0 +1,89 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +import dayjs from "dayjs"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const radioDict = dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }); + return { + radioDict, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + buttons: { + edit: { dropdown: true }, + remove: { dropdown: true } + }, + width: 630 + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + like: { + title: "like", + type: "number", + search: { show: true } + }, + switch: { + title: "switch", + type: "dict-switch", + dict: dict({ + data: [ + { value: true, label: "开启" }, + { value: false, label: "关闭" } + ] + }) + }, + createDate: { + title: "时间", + type: "datetime", + column: { + align: "left", + width: 300 + }, + valueBuilder({ key, row }) { + row[key] = dayjs(row[key]); + } + }, + updateDate: { + title: "修改时间", + type: "datetime", + column: { + show: false + }, + valueBuilder({ key, row }) { + row[key] = dayjs(row[key]); + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/slots/cell/index.vue b/packages/ui/certd-client/src/views/crud/slots/cell/index.vue new file mode 100644 index 000000000..9b8b43494 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/cell/index.vue @@ -0,0 +1,79 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/slots/cell/mock.js b/packages/ui/certd-client/src/views/crud/slots/cell/mock.js new file mode 100644 index 000000000..aa2397b79 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/cell/mock.js @@ -0,0 +1,26 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "SlotsCell", + idGenerator: 0 +}; +const list = [ + { + like: 10000, + switch: true, + createDate: new Date().getTime(), + updateDate: new Date().getTime() + }, + { + like: 10000, + switch: false, + createDate: new Date().getTime(), + updateDate: new Date().getTime() + }, + { + like: 10000, + switch: true + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/slots/form-item/api.js b/packages/ui/certd-client/src/views/crud/slots/form-item/api.js new file mode 100644 index 000000000..924d6570b --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/form-item/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/SlotsFormItem"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/slots/form-item/crud.jsx b/packages/ui/certd-client/src/views/crud/slots/form-item/crud.jsx new file mode 100644 index 000000000..7af63d446 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/form-item/crud.jsx @@ -0,0 +1,51 @@ +import * as api from "./api"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + topics: { + title: "多行输入", + type: "text", + search: { show: true }, + form: { + rules: [{ required: true, message: "请输入" }] + }, + column: { + component: { name: "fs-values-format" } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/slots/form-item/index.vue b/packages/ui/certd-client/src/views/crud/slots/form-item/index.vue new file mode 100644 index 000000000..97869b958 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/form-item/index.vue @@ -0,0 +1,72 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/slots/form-item/mock.js b/packages/ui/certd-client/src/views/crud/slots/form-item/mock.js new file mode 100644 index 000000000..a9f3d7569 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/form-item/mock.js @@ -0,0 +1,13 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "SlotsFormItem", + idGenerator: 0 +}; +const list = [ + { + topics: ["fast-crud 666", "fast-crud真好用"] + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/slots/form/api.js b/packages/ui/certd-client/src/views/crud/slots/form/api.js new file mode 100644 index 000000000..b15ec4e05 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/form/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/SlotsForm"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/slots/form/crud.jsx b/packages/ui/certd-client/src/views/crud/slots/form/crud.jsx new file mode 100644 index 000000000..b5d8c1df5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/form/crud.jsx @@ -0,0 +1,50 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + const radioDict = dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }); + return { + radioDict, + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + text: { + title: "文本", + type: "text", + search: { show: true } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/slots/form/index.vue b/packages/ui/certd-client/src/views/crud/slots/form/index.vue new file mode 100644 index 000000000..74aa9ec35 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/form/index.vue @@ -0,0 +1,69 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/slots/form/mock.js b/packages/ui/certd-client/src/views/crud/slots/form/mock.js new file mode 100644 index 000000000..22d45cf72 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/form/mock.js @@ -0,0 +1,13 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "SlotsForm", + idGenerator: 0 +}; +const list = [ + { + text: "文本输入" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/slots/layout/api.js b/packages/ui/certd-client/src/views/crud/slots/layout/api.js new file mode 100644 index 000000000..56f3de45f --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/layout/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/SlotsLayout"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/slots/layout/crud.jsx b/packages/ui/certd-client/src/views/crud/slots/layout/crud.jsx new file mode 100644 index 000000000..af8ad45e2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/layout/crud.jsx @@ -0,0 +1,49 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async (id) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/slots/layout/index.vue b/packages/ui/certd-client/src/views/crud/slots/layout/index.vue new file mode 100644 index 000000000..ce35fe282 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/layout/index.vue @@ -0,0 +1,77 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/slots/layout/mock.js b/packages/ui/certd-client/src/views/crud/slots/layout/mock.js new file mode 100644 index 000000000..31e3a7d78 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/layout/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "SlotsLayout", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/crud/slots/search/api.js b/packages/ui/certd-client/src/views/crud/slots/search/api.js new file mode 100644 index 000000000..192ff43ea --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/search/api.js @@ -0,0 +1,42 @@ +import { requestForMock } from "/src/api/service"; +const request = requestForMock; +const apiPrefix = "/mock/SlotsSearch"; +export function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "get", + data: query + }); +} + +export function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "get", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/crud/slots/search/crud.jsx b/packages/ui/certd-client/src/views/crud/slots/search/crud.jsx new file mode 100644 index 000000000..af8ad45e2 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/search/crud.jsx @@ -0,0 +1,49 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async (id) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 50 + }, + form: { + show: false + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }) + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/crud/slots/search/index.vue b/packages/ui/certd-client/src/views/crud/slots/search/index.vue new file mode 100644 index 000000000..bbf30cffb --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/search/index.vue @@ -0,0 +1,54 @@ + + + diff --git a/packages/ui/certd-client/src/views/crud/slots/search/mock.js b/packages/ui/certd-client/src/views/crud/slots/search/mock.js new file mode 100644 index 000000000..4312767d5 --- /dev/null +++ b/packages/ui/certd-client/src/views/crud/slots/search/mock.js @@ -0,0 +1,19 @@ +import mockUtil from "/src/mock/base"; +const options = { + name: "SlotsSearch", + idGenerator: 0 +}; +const list = [ + { + radio: "1" + }, + { + radio: "2" + }, + { + radio: "0" + } +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/packages/ui/certd-client/src/views/framework/error/404.vue b/packages/ui/certd-client/src/views/framework/error/404.vue new file mode 100644 index 000000000..2dacd3630 --- /dev/null +++ b/packages/ui/certd-client/src/views/framework/error/404.vue @@ -0,0 +1,18 @@ + + + diff --git a/packages/ui/certd-client/src/views/framework/home/index.vue b/packages/ui/certd-client/src/views/framework/home/index.vue new file mode 100644 index 000000000..274457799 --- /dev/null +++ b/packages/ui/certd-client/src/views/framework/home/index.vue @@ -0,0 +1,15 @@ + + + + diff --git a/packages/ui/certd-client/src/views/framework/home/page-cover/helper.js b/packages/ui/certd-client/src/views/framework/home/page-cover/helper.js new file mode 100644 index 000000000..38334ac1d --- /dev/null +++ b/packages/ui/certd-client/src/views/framework/home/page-cover/helper.js @@ -0,0 +1,31 @@ +export default { + crud: ` columns: { + date:{ + title: '姓名', //字段名称 + type: 'text', //字段类型,添加、修改、查询将自动生成相应表单组件 + }, + province: { + title: '城市', + type: 'dict-select', //选择框 + form: { //表单组件自定义配置,此处配置选择框为多选 + component: { //支持任何v-model组件 + filterable: true, multiple: true, clearable: true + } + }, + dict: dict({ + data: [ //本地数据字典 + { value: 'sz', label: '深圳' }, + { value: 'gz', label: '广州' }, + { value: 'wh', label: '武汉' }, + { value: 'sh', label: '上海' } + ] + }) + }, + status: { + title: '状态', + type: 'dict-select', //选择框,默认单选 + dict: dict({ url: '/dicts/OpenStatusEnum' })//远程数据字典 + }, + } + ` +}; diff --git a/packages/ui/certd-client/src/views/framework/home/page-cover/image/crud.png b/packages/ui/certd-client/src/views/framework/home/page-cover/image/crud.png new file mode 100644 index 000000000..3dfb047dd Binary files /dev/null and b/packages/ui/certd-client/src/views/framework/home/page-cover/image/crud.png differ diff --git a/packages/ui/certd-client/src/views/framework/home/page-cover/image/darkblue@2x.png b/packages/ui/certd-client/src/views/framework/home/page-cover/image/darkblue@2x.png new file mode 100644 index 000000000..efe1577f4 Binary files /dev/null and b/packages/ui/certd-client/src/views/framework/home/page-cover/image/darkblue@2x.png differ diff --git a/packages/ui/certd-client/src/views/framework/home/page-cover/image/logo.svg b/packages/ui/certd-client/src/views/framework/home/page-cover/image/logo.svg new file mode 100644 index 000000000..7a33425e3 --- /dev/null +++ b/packages/ui/certd-client/src/views/framework/home/page-cover/image/logo.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/certd-client/src/views/framework/home/page-cover/index.vue b/packages/ui/certd-client/src/views/framework/home/page-cover/index.vue new file mode 100644 index 000000000..5c9241fa6 --- /dev/null +++ b/packages/ui/certd-client/src/views/framework/home/page-cover/index.vue @@ -0,0 +1,137 @@ + + + diff --git a/packages/ui/certd-client/src/views/framework/login/index.vue b/packages/ui/certd-client/src/views/framework/login/index.vue new file mode 100644 index 000000000..a4b8728b7 --- /dev/null +++ b/packages/ui/certd-client/src/views/framework/login/index.vue @@ -0,0 +1,270 @@ + + + + diff --git a/packages/ui/certd-client/src/views/framework/register/index.vue b/packages/ui/certd-client/src/views/framework/register/index.vue new file mode 100644 index 000000000..d63e8e102 --- /dev/null +++ b/packages/ui/certd-client/src/views/framework/register/index.vue @@ -0,0 +1,178 @@ + + + + diff --git a/packages/ui/certd-client/src/views/sys/authority/permission/api.js b/packages/ui/certd-client/src/views/sys/authority/permission/api.js new file mode 100644 index 000000000..64627ea60 --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/permission/api.js @@ -0,0 +1,48 @@ +import { request } from "/src/api/service"; +const apiPrefix = "/sys/authority/permission"; +export async function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "post", + data: query + }); +} + +export async function GetTree() { + return request({ + url: apiPrefix + "/tree", + method: "post" + }); +} + +export async function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export async function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export async function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export async function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "post", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/sys/authority/permission/crud.jsx b/packages/ui/certd-client/src/views/sys/authority/permission/crud.jsx new file mode 100644 index 000000000..c8b918534 --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/permission/crud.jsx @@ -0,0 +1,150 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; + +export default function ({ expose }) { + const pageRequest = async (query) => { + const list = await api.GetTree(query); + + return { + current: 1, + records: list, + total: 10000, + pageSize: 10000 + }; + }; + + async function afterChange() { + await permissionTreeDict.reloadDict(); + } + const editRequest = async ({ form, row }) => { + form.id = row.id; + const ret = await api.UpdateObj(form); + await afterChange(); + return ret; + }; + const delRequest = async ({ row }) => { + const ret = await api.DelObj(row.id); + await afterChange(); + return ret; + }; + + const addRequest = async ({ form }) => { + const ret = await api.AddObj(form); + await afterChange(); + return ret; + }; + let permissionTreeDict = dict({ + url: "/sys/authority/permission/tree", + isTree: true, + value: "id", + label: "title", + async onReady({ dict }) { + dict.setData([{ id: -1, title: "根节点", children: dict.data }]); + } + }); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + actionbar: { + show: false + }, + toolbar: { + show: false + }, + table: { + show: false + // scroll: { fixed: true } + }, + rowHandle: { + fixed: "right" + }, + search: { + show: false + }, + pagination: { + show: false, + pageSize: 100000 + }, + columns: { + id: { + title: "id", + type: "number", + form: { show: false }, // 表单配置 + column: { + width: 120, + sortable: "custom" + } + }, + title: { + title: "权限名称", + type: "text", + form: { + rules: [ + { required: true, message: "请输入权限名称" }, + { max: 50, message: "最大50个字符" } + ], + component: { + placeholder: "权限名称" + } + }, + column: { + width: 200 + } + }, + + permission: { + title: "权限代码", + type: "text", + column: { + width: 170 + }, + form: { + rules: [ + { required: true, message: "请输入权限代码" }, + { max: 100, message: "最大100个字符" } + ], + component: { + placeholder: "例如:sys:user:view" + } + } + }, + sort: { + title: "排序", + type: "number", + column: { + width: 100 + }, + form: { + value: 100, + rules: [{ required: true, type: "number", message: "排序号必填" }] + } + }, + parentId: { + title: "父节点", + type: "dict-tree", + column: { + width: 100 + }, + dict: permissionTreeDict, + form: { + value: -1, + component: { + multiple: false, + defaultExpandAll: true, + dict: { cache: false }, + fieldNames: { + value: "id", + label: "title" + } + } + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/sys/authority/permission/fs-permission-tree.vue b/packages/ui/certd-client/src/views/sys/authority/permission/fs-permission-tree.vue new file mode 100644 index 000000000..692ca1ddb --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/permission/fs-permission-tree.vue @@ -0,0 +1,178 @@ + + + + + diff --git a/packages/ui/certd-client/src/views/sys/authority/permission/index.vue b/packages/ui/certd-client/src/views/sys/authority/permission/index.vue new file mode 100644 index 000000000..0005298e3 --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/permission/index.vue @@ -0,0 +1,90 @@ + + + + diff --git a/packages/ui/certd-client/src/views/sys/authority/role/api.js b/packages/ui/certd-client/src/views/sys/authority/role/api.js new file mode 100644 index 000000000..6793b6fa3 --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/role/api.js @@ -0,0 +1,70 @@ +import { request } from "/src/api/service"; +const apiPrefix = "/sys/authority/role"; +export async function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "post", + data: query + }); +} + +export async function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export async function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export async function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export async function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "post", + params: { id } + }); +} + +/** + * 获取角色权限资源 + * @param roleId + * @returns {*} + * @constructor + */ +export function getPermissionIds(roleId) { + return request({ + url: apiPrefix + "/getPermissionIds", + method: "post", + params: { id: roleId } + }); +} + +/** + * 授权 + * @param roleId + * @param permissionIds + * @returns {*} + * @constructor + */ +export function DoAuthz(roleId, permissionIds) { + return request({ + url: apiPrefix + "/authz", + method: "post", + data: { roleId, permissionIds } + }); +} diff --git a/packages/ui/certd-client/src/views/sys/authority/role/crud.jsx b/packages/ui/certd-client/src/views/sys/authority/role/crud.jsx new file mode 100644 index 000000000..a4c7290ef --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/role/crud.jsx @@ -0,0 +1,81 @@ +import * as api from "./api"; +export default function ({ expose, authz }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + width: 300, + buttons: { + authz: { + type: "link", + text: "授权", + async click(context) { + await authz.authzOpen(context.record.id); + } + } + } + }, + columns: { + id: { + title: "id", + form: { show: false }, // 表单配置 + column: { + width: 70, + sorter: true + } + }, + name: { + title: "角色名称", + type: "text", + search: { show: true }, + form: { + rules: [ + { required: true, message: "请输入角色名称" }, + { max: 50, message: "最大50个字符" } + ] + }, // 表单配置 + column: { + sorter: true + } + }, + createTime: { + title: "创建时间", + type: "datetime", + column: { + sorter: true + }, + form: { + show: false + } + }, + updateTime: { + title: "更新时间", + type: "datetime", + column: { + sorter: true + }, + form: { show: false } // 表单配置 + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/sys/authority/role/index.vue b/packages/ui/certd-client/src/views/sys/authority/role/index.vue new file mode 100644 index 000000000..089cf1cdd --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/role/index.vue @@ -0,0 +1,133 @@ + + + diff --git a/packages/ui/certd-client/src/views/sys/authority/user/api.js b/packages/ui/certd-client/src/views/sys/authority/user/api.js new file mode 100644 index 000000000..55474d504 --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/user/api.js @@ -0,0 +1,41 @@ +import { request } from "/src/api/service"; +const apiPrefix = "/sys/authority/user"; +export async function GetList(query) { + return request({ + url: apiPrefix + "/page", + method: "post", + data: query + }); +} + +export async function AddObj(obj) { + return request({ + url: apiPrefix + "/add", + method: "post", + data: obj + }); +} + +export async function UpdateObj(obj) { + return request({ + url: apiPrefix + "/update", + method: "post", + data: obj + }); +} + +export async function DelObj(id) { + return request({ + url: apiPrefix + "/delete", + method: "post", + params: { id } + }); +} + +export async function GetObj(id) { + return request({ + url: apiPrefix + "/info", + method: "post", + params: { id } + }); +} diff --git a/packages/ui/certd-client/src/views/sys/authority/user/crud.jsx b/packages/ui/certd-client/src/views/sys/authority/user/crud.jsx new file mode 100644 index 000000000..291e31f35 --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/user/crud.jsx @@ -0,0 +1,146 @@ +import * as api from "./api"; +import { dict } from "@fast-crud/fast-crud"; +export default function ({ expose }) { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + fixed: "right" + }, + table: { + scroll: { + //使用固定列时需要设置此值,并且大于等于列宽度之和的值 + x: 1400 + } + }, + columns: { + id: { + title: "id", + form: { show: false }, // 表单配置 + column: { + width: 70, + sorter: true + } + }, + createTime: { + title: "创建时间", + type: "datetime", + form: { show: false }, // 表单配置 + column: { + width: 180, + sorter: true + } + }, + // updateTime: { + // title: "修改时间", + // type: "datetime", + // form: { show: false }, // 表单配置 + // column: { + // sortable: "update_time", + // width: 180 + // } + // }, + username: { + title: "用户名", + type: "text", + search: { show: true }, // 开启查询 + form: { + rules: [ + { required: true, message: "请输入用户名" }, + { max: 50, message: "最大50个字符" } + ] + }, + editForm: { component: { disabled: true } }, + column: { + sorter: true + } + }, + password: { + title: "密码", + type: "text", + key: "password", + column: { + show: false + }, + form: { + rules: [{ max: 50, message: "最大50个字符" }], + component: { + showPassword: true + }, + helper: "填写则修改密码" + } + }, + nickName: { + title: "昵称", + type: "text", + search: { show: true }, // 开启查询 + form: { + rules: [{ max: 50, message: "最大50个字符" }] + }, + column: { + sorter: true + } + }, + avatar: { + title: "头像", + type: "cropper-uploader", + column: { + width: 100, + component: { + //设置高度,修复操作列错位的问题 + style: { + height: "30px", + width: "auto" + } + } + } + }, + remark: { + title: "备注", + type: "text", + column: { + sorter: true + }, + form: { + rules: [{ max: 100, message: "最大100个字符" }] + } + }, + roles: { + title: "角色", + type: "dict-select", + dict: dict({ + url: "/sys/authority/role/list", + value: "id", + label: "name" + }), // 数据字典 + form: { + component: { mode: "multiple" } + }, + column: { + width: 250, + sortable: true + } + } + } + } + }; +} diff --git a/packages/ui/certd-client/src/views/sys/authority/user/index.vue b/packages/ui/certd-client/src/views/sys/authority/user/index.vue new file mode 100644 index 000000000..33f0c7a55 --- /dev/null +++ b/packages/ui/certd-client/src/views/sys/authority/user/index.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/ui/certd-client/tailwind.config.js b/packages/ui/certd-client/tailwind.config.js new file mode 100644 index 000000000..ec39f7e53 --- /dev/null +++ b/packages/ui/certd-client/tailwind.config.js @@ -0,0 +1,11 @@ +module.exports = { + purge: [], + darkMode: false, // or 'media' or 'class' + theme: { + extend: {} + }, + variants: { + extend: {} + }, + plugins: [] +}; diff --git a/packages/ui/certd-client/tests/unit/example.spec.ts b/packages/ui/certd-client/tests/unit/example.spec.ts new file mode 100644 index 000000000..bc9939b65 --- /dev/null +++ b/packages/ui/certd-client/tests/unit/example.spec.ts @@ -0,0 +1,13 @@ +import { expect } from 'chai' +import { shallowMount } from '@vue/test-utils' +import HelloWorld from '@/components/HelloWorld.vue' + +describe('HelloWorld.vue', () => { + it('renders props.msg when passed', () => { + const msg = 'new message' + const wrapper = shallowMount(HelloWorld, { + props: { msg } + }) + expect(wrapper.text()).to.include(msg) + }) +}) diff --git a/packages/ui/certd-client/tsconfig.json b/packages/ui/certd-client/tsconfig.json new file mode 100644 index 000000000..bdb44e0fe --- /dev/null +++ b/packages/ui/certd-client/tsconfig.json @@ -0,0 +1,45 @@ +{ + "compilerOptions": { + // 这样就可以对 `this` 上的数据属性进行更严格的推断` + "noImplicitAny": false, + "target": "esnext", + "module": "esnext", + "strict": true, + "jsx": "preserve", + "importHelpers": true, + "moduleResolution": "node", + "experimentalDecorators": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceMap": true, + "baseUrl": ".", + "outDir": "./dist/ts", + "types": [ + "mocha", + "chai", + "node" + ], + "paths": { + "/@/*": ["src/*"], + "/src/*": ["src/*"], + "/#/*": ["types/*"] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue", + "tests/**/*.ts", + "tests/**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/packages/ui/certd-client/vite.config.ts b/packages/ui/certd-client/vite.config.ts new file mode 100644 index 000000000..de89cd4be --- /dev/null +++ b/packages/ui/certd-client/vite.config.ts @@ -0,0 +1,101 @@ +import vue from "@vitejs/plugin-vue"; +import vueJsx from "@vitejs/plugin-vue-jsx"; +import visualizer from "rollup-plugin-visualizer"; +import viteCompression from "vite-plugin-compression"; +import PurgeIcons from "vite-plugin-purge-icons"; +import * as path from "path"; +// import WindiCSS from "vite-plugin-windicss"; +// import { generateModifyVars } from "./build/modify-vars"; +// import { configThemePlugin } from "./build/theme-plugin"; +// import OptimizationPersist from "vite-plugin-optimize-persist"; +// import PkgConfig from "vite-plugin-package-config"; +// https://vitejs.dev/config/ +// 增加环境变量 _ +process.env.VITE_APP_VERSION = require("./package.json").version; +process.env.VITE_APP_BUILD_TIME = require("dayjs")().format("YYYY-M-D HH:mm:ss"); + +export default ({ command, mode }) => { + console.log("args", command, mode); + + let devServerFs: any = {}; + let devAlias: any[] = []; + if (mode.startsWith("debug")) { + devAlias = [ + { find: /@fast-crud\/fast-crud\/dist/, replacement: path.resolve("../../fast-crud/src/") }, + { find: /@fast-crud\/fast-crud$/, replacement: path.resolve("../../fast-crud/src/") }, + { find: /@fast-crud\/fast-extends\/dist/, replacement: path.resolve("../../fast-extends/src/") }, + { find: /@fast-crud\/fast-extends$/, replacement: path.resolve("../../fast-extends/src/") }, + { find: /@fast-crud\/ui-antdv$/, replacement: path.resolve("../../ui/ui-antdv/src/") } + ]; + devServerFs = { + // 如果是你自己的项目,这项可以删掉 + // 这里配置dev启动时读取的项目根目录 + allow: ["../../"] + }; + console.log("devAlias", devAlias); + } + + return { + base: "/", + plugins: [ + vueJsx(), + vue(), + // 压缩build后的代码 + viteCompression(), + PurgeIcons({ + // iconSource: "local" + // remoteDataAPI: "https://gitee.com/fast-crud/collections-json/raw/master/json", + // includedCollections: ["ion"] + }) + //主题色替换 + //...configThemePlugin(true), + // viteThemePlugin({ + // // Match the color to be modified + // colorVariables: ["#1890ff", "#40a9ff"] + // }), + // windicss tailwindcss + // WindiCSS() + ], + esbuild: { + // pure: ["console.log", "debugger"], + jsxFactory: "h", + jsxFragment: "Fragment" + }, + resolve: { + alias: [ + ...devAlias, + { find: "/@", replacement: path.resolve("./src") }, + { find: "/#", replacement: path.resolve("./types") } + ], + dedupe: ["vue"] + }, + optimizeDeps: { + include: ["ant-design-vue"] + }, + build: { + rollupOptions: { + plugins: [visualizer()] + } + }, + css: { + preprocessorOptions: { + less: { + // 修改默认主题颜色,配置less变量 + // modifyVars: generateModifyVars(), + javascriptEnabled: true + } + } + }, + server: { + port: 3002, + fs: devServerFs, + proxy: { + // with options + "/api": { + //配套后端 https://github.com/fast-crud/fs-server-js + target: "http://127.0.0.1:7001" + } + } + } + }; +}; diff --git a/packages/ui/certd-client/windi.config.js b/packages/ui/certd-client/windi.config.js new file mode 100644 index 000000000..6d82c4951 --- /dev/null +++ b/packages/ui/certd-client/windi.config.js @@ -0,0 +1,6 @@ +// windi.config.ts +export default { + attributify: { + prefix: "w:" + } +}; diff --git a/packages/ui/certd-server/.dockerignore b/packages/ui/certd-server/.dockerignore new file mode 100644 index 000000000..6eb6e80d1 --- /dev/null +++ b/packages/ui/certd-server/.dockerignore @@ -0,0 +1,16 @@ +logs/ +npm-debug.log +yarn-error.log +node_modules/ +package-lock.json +yarn.lock +coverage/ +!dist/ +.idea/ +run/ +.DS_Store +*.sw* +*.un~ +.tsbuildinfo +.tsbuildinfo.* +/data/db.sqlite diff --git a/packages/ui/certd-server/.editorconfig b/packages/ui/certd-server/.editorconfig new file mode 100644 index 000000000..4c7f8a8ef --- /dev/null +++ b/packages/ui/certd-server/.editorconfig @@ -0,0 +1,11 @@ +# 🎨 editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/packages/ui/certd-server/.eslintrc.json b/packages/ui/certd-server/.eslintrc.json new file mode 100644 index 000000000..7828b2c51 --- /dev/null +++ b/packages/ui/certd-server/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "extends": "./node_modules/mwts/", + "ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"], + "env": { + "jest": true + }, + "rules": { + "@typescript-eslint/ban-ts-ignore": "off", + "@typescript-eslint/ban-ts-comment": "off" + } +} diff --git a/packages/ui/certd-server/.gitignore b/packages/ui/certd-server/.gitignore new file mode 100644 index 000000000..41f712631 --- /dev/null +++ b/packages/ui/certd-server/.gitignore @@ -0,0 +1,18 @@ +logs/ +npm-debug.log +yarn-error.log +node_modules/ +package-lock.json +yarn.lock +coverage/ +dist/ +.idea/ +run/ +.DS_Store +*.sw* +*.un~ +.tsbuildinfo +.tsbuildinfo.* +/data/db.sqlite +/pnpm-lock.yaml +.serverless diff --git a/packages/ui/certd-server/.prettierrc.js b/packages/ui/certd-server/.prettierrc.js new file mode 100644 index 000000000..b964930f3 --- /dev/null +++ b/packages/ui/certd-server/.prettierrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('mwts/.prettierrc.json') +} diff --git a/packages/ui/certd-server/Dockerfile b/packages/ui/certd-server/Dockerfile new file mode 100644 index 000000000..521407343 --- /dev/null +++ b/packages/ui/certd-server/Dockerfile @@ -0,0 +1,15 @@ +FROM registry.cn-shenzhen.aliyuncs.com/greper/node:15.8.0-alpine + +WORKDIR /home + +COPY . . +# 如果各公司有自己的私有源,可以替换registry地址 +#RUN npm install --registry=https://registry.npmmirror.com +RUN npm install -g cnpm +RUN cnpm install +RUN npm run build:preview + +# 如果端口更换,这边可以更新一下 +EXPOSE 7001 + +CMD ["npm", "run", "online:preview"] diff --git a/packages/ui/certd-server/LICENSE b/packages/ui/certd-server/LICENSE new file mode 100644 index 000000000..0ad25db4b --- /dev/null +++ b/packages/ui/certd-server/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/packages/ui/certd-server/README.md b/packages/ui/certd-server/README.md new file mode 100644 index 000000000..d0aca2df6 --- /dev/null +++ b/packages/ui/certd-server/README.md @@ -0,0 +1,31 @@ +# fast-server-js + +base on midway + +## QuickStart + + + +see [midway docs][midway] for more detail. + +### Development + +```bash +$ npm i +$ npm run dev +$ open http://localhost:7001/ +``` + +### Deploy + +```bash +$ npm start +``` + +### npm scripts + +- Use `npm run lint` to check code style. +- Use `npm test` to run unit test. + + +[midway]: https://midwayjs.org diff --git a/packages/ui/certd-server/README.zh-CN.md b/packages/ui/certd-server/README.zh-CN.md new file mode 100644 index 000000000..54b99c0e4 --- /dev/null +++ b/packages/ui/certd-server/README.zh-CN.md @@ -0,0 +1,30 @@ +# fast-server-js + +## 快速入门 + + + +如需进一步了解,参见 [midway 文档][midway]。 + +### 本地开发 + +```bash +$ npm i +$ npm run dev +$ open http://localhost:7001/ +``` + +### 部署 + +```bash +$ npm start +``` + +### 内置指令 + +- 使用 `npm run lint` 来做代码风格检查。 +- 使用 `npm test` 来执行单元测试。 + + +[midway]: https://midwayjs.org + diff --git a/packages/ui/certd-server/app.js b/packages/ui/certd-server/app.js new file mode 100644 index 000000000..7f1d93539 --- /dev/null +++ b/packages/ui/certd-server/app.js @@ -0,0 +1,23 @@ +// 获取框架 +const WebFramework = require('@midwayjs/koa').Framework; +const { Bootstrap } = require('@midwayjs/bootstrap'); + +const DirectoryFileDetector = require( "@midwayjs/core").DirectoryFileDetector; + +const baseDir = process.cwd() +const pipelineDir = baseDir +"./node_modules/@certd/pipeline" +const customFileDetector = new DirectoryFileDetector({loadDir:[baseDir,pipelineDir]}) + + +module.exports = async () => { + // 加载框架并执行 + await Bootstrap.configure({ + moduleDetector:customFileDetector + }).run(); + // 获取依赖注入容器 + const container = Bootstrap.getApplicationContext(); + // 获取 koa framework + const framework = container.get(WebFramework); + // 返回 app 对象 + return framework.getApplication(); +}; diff --git a/packages/ui/certd-server/bootstrap.js b/packages/ui/certd-server/bootstrap.js new file mode 100644 index 000000000..1b0bbb73a --- /dev/null +++ b/packages/ui/certd-server/bootstrap.js @@ -0,0 +1,15 @@ +const WebFramework = require('@midwayjs/koa').Framework; +const web = new WebFramework().configure({ + port: 7001, +}); + +const DirectoryFileDetector = require( "@midwayjs/core").DirectoryFileDetector; + +const baseDir = process.cwd() +const pipelineDir = baseDir +"./node_modules/@certd/pipeline" +const customFileDetector = new DirectoryFileDetector({loadDir:[baseDir,pipelineDir]}) + +const { Bootstrap } = require('@midwayjs/bootstrap'); +Bootstrap.load(web).configure({ + moduleDetector:customFileDetector +}).run(); diff --git a/packages/ui/certd-server/db/migration/v00001__init.sql b/packages/ui/certd-server/db/migration/v00001__init.sql new file mode 100644 index 000000000..b8f169c35 --- /dev/null +++ b/packages/ui/certd-server/db/migration/v00001__init.sql @@ -0,0 +1,77 @@ +-- 表:sys_permission +CREATE TABLE "sys_permission" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(100) NOT NULL, "permission" varchar(100), "parent_id" integer NOT NULL DEFAULT (-1), "sort" integer NOT NULL, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (1, '系统管理', 'sys', -1, 1, 1, 1624085863636); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (2, '权限管理', 'sys:auth', 1, 1, 1, 1); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (3, '用户管理', 'sys:auth:user', 2, 1, 1, 1); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (4, '查看', 'sys:auth:user:view', 3, 100, 1, 1624189112333); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (5, '权限管理', 'sys:auth:per', 2, 1, 1, 1); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (6, '查看', 'sys:auth:per:view', 5, 100, 1, 1624189161317); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (7, '角色管理', 'sys:auth:role', 2, 1, 1, 1); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (8, '查看', 'sys:auth:role:view', 7, 1, 1, 1); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (9, '修改', 'sys:auth:user:edit', 3, 300, 1, 1624189127688); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (10, '删除', 'sys:auth:user:remove', 3, 400, 1, 1624189133184); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (11, '添加', 'sys:auth:user:add', 3, 200, 1, 1624189142576); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (12, '修改', 'sys:auth:role:edit', 7, 1, 1, 1); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (13, '删除', 'sys:auth:role:remove', 7, 1, 1, 1); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (14, '添加', 'sys:auth:role:add', 7, 1, 1, 1); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (15, '修改', 'sys:auth:per:edit', 5, 300, 1, 1624189308837); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (16, '删除', 'sys:auth:per:remove', 5, 400, 1, 1624189256926); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (17, '添加', 'sys:auth:per:add', 5, 200, 1, 1624189283766); +INSERT INTO sys_permission (id, title, permission, parent_id, sort, create_time, update_time) VALUES (18,'授权','sys:auth:role:authz',7,100,1,1624335712144); + + + +-- 表:sys_role +CREATE TABLE "sys_role" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(100) NOT NULL, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); +INSERT INTO sys_role (id, name, create_time, update_time) VALUES (1, '管理员', 1, 1623749138537); +INSERT INTO sys_role (id, name, create_time, update_time) VALUES (2, '只读角色', 1, 1623749138537); + +-- 表:sys_role_permission +CREATE TABLE "sys_role_permission" ("role_id" integer NOT NULL, "permission_id" integer NOT NULL, PRIMARY KEY ("role_id", "permission_id")); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 1); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 2); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 3); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 4); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 5); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 6); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 7); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 8); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 9); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 10); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 11); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 12); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 13); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 14); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 15); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 16); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 17); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, 18); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (1, -1); + +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 4); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 6); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 8); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 1); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 2); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 3); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 5); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, 7); +INSERT INTO sys_role_permission (role_id, permission_id) VALUES (2, -1); + +-- 表:sys_user +CREATE TABLE "sys_user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "username" varchar(100) NOT NULL, "password" varchar(50) NOT NULL, "nick_name" varchar(50), "avatar" varchar(255), "phone_code" varchar(20), "mobile" varchar(20), "email" varchar(100),"remark" varchar(100), "status" integer NOT NULL DEFAULT (1), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); +INSERT INTO sys_user (id, username, password, nick_name, avatar, phone_code, mobile, email, status, create_time, update_time,remark) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 'admin', NULL, NULL, NULL, NULL, 1, 2011123132, 123132,NULL); +INSERT INTO sys_user (id, username, password, nick_name, avatar, phone_code, mobile, email, status, create_time, update_time,remark) VALUES (2, 'readonly', 'e10adc3949ba59abbe56e057f20f883e', '只读用户', NULL, NULL, NULL, NULL, 1, 2011123132, 123132,'密码:123456'); + +-- 表:sys_user_role +CREATE TABLE "sys_user_role" ("role_id" integer NOT NULL, "user_id" integer NOT NULL, PRIMARY KEY ("role_id", "user_id")); +INSERT INTO sys_user_role (role_id, user_id) VALUES (1, 1); +INSERT INTO sys_user_role (role_id, user_id) VALUES (2, 2); + +-- 索引:IDX_223de54d6badbe43a5490450c3 +CREATE UNIQUE INDEX "IDX_223de54d6badbe43a5490450c3" ON "sys_role" ("name"); + +-- 索引:IDX_9e7164b2f1ea1348bc0eb0a7da +CREATE UNIQUE INDEX "IDX_9e7164b2f1ea1348bc0eb0a7da" ON "sys_user" ("username"); + + diff --git a/packages/ui/certd-server/db/migration/v00002__for_pre.sql b/packages/ui/certd-server/db/migration/v00002__for_pre.sql new file mode 100644 index 000000000..708d7aed5 --- /dev/null +++ b/packages/ui/certd-server/db/migration/v00002__for_pre.sql @@ -0,0 +1,4 @@ +-- for preview 限制演示环境的数据修改 +update sqlite_sequence set seq = 1000 where name = 'sys_user' ; +update sqlite_sequence set seq = 1000 where name = 'sys_permission' ; +update sqlite_sequence set seq = 1000 where name = 'sys_role' ; diff --git a/packages/ui/certd-server/db/migration/v10000__certd.sql b/packages/ui/certd-server/db/migration/v10000__certd.sql new file mode 100644 index 000000000..c3867d6b0 --- /dev/null +++ b/packages/ui/certd-server/db/migration/v10000__certd.sql @@ -0,0 +1,24 @@ +-- +-- 由SQLiteStudio v3.3.3 产生的文件 周六 7月 3 00:38:02 2021 +-- +-- 文本编码:UTF-8 +-- + +-- 表:cd_access +CREATE TABLE "cd_access" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "name" varchar(100) NOT NULL, "type" varchar(100) NOT NULL, "setting" varchar(1024), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); + +-- 表:cd_cert +CREATE TABLE "cd_cert" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "domains" varchar(2048) NOT NULL, "email" varchar(100) NOT NULL, "cert_issuer_id" integer, "challenge_type" varchar(100), "challenge_dns_type" varchar(100),"challenge_access_id" integer, "country" varchar(100), "state" varchar(100), "locality" varchar(100), "organization" varchar(100), "organization_unit" varchar(100), "remark" varchar(100), "last_history_id" integer, "last_success_id" integer, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); + +-- 表:cd_cert_apply_history +CREATE TABLE "cd_cert_apply_history" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "cert_id" integer NOT NULL, "success" boolean, "result" varchar(1024), "cert_crt" varchar(1024), "cert_key" varchar(1024), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); + +-- 表:cd_cert_issuer +CREATE TABLE "cd_cert_issuer" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "type" varchar(20) NOT NULL, "account" varchar(100) NOT NULL, "private_key" varchar(1024), "setting" varchar, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); + +-- 表:cd_task +CREATE TABLE "cd_task" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "name" varchar(100), "type" varchar(100), "setting" varchar(2048), "cert_id" integer NOT NULL, "last_history_id" integer, "last_success_id" integer, "remark" varchar(100), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); + +-- 表:cd_task_history +CREATE TABLE "cd_task_history" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "task_id" integer NOT NULL, "cert_id" integer NOT NULL, "cert_apply_history_id" integer NOT NULL, "success" boolean, "result" varchar(2048), "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)); + diff --git a/packages/ui/certd-server/f.yaml b/packages/ui/certd-server/f.yaml new file mode 100644 index 000000000..a63871126 --- /dev/null +++ b/packages/ui/certd-server/f.yaml @@ -0,0 +1,11 @@ +service: certd ## 应用发布到云平台的名字,一般指应用名 + +provider: + name: aliyun ## 发布的云平台,aliyun,tencent 等 + +deployType: + type: koa ## 部署的应用类型 + version: 3.0.0 +custom: + customDomain: + domainName: auto diff --git a/packages/ui/certd-server/jest.config.js b/packages/ui/certd-server/jest.config.js new file mode 100644 index 000000000..c5bd388a7 --- /dev/null +++ b/packages/ui/certd-server/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testPathIgnorePatterns: ['/test/fixtures'], + coveragePathIgnorePatterns: ['/test/'], +}; \ No newline at end of file diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json new file mode 100644 index 000000000..bd8a55d53 --- /dev/null +++ b/packages/ui/certd-server/package.json @@ -0,0 +1,91 @@ +{ + "name": "@fast-crud/fs-server-js", + "version": "0.2.0", + "description": "fast-server base midway", + "private": true, + "midway-integration": { + "lifecycle": { + "before:package:cleanup": "npm run build" + } + }, + "scripts": { + "deploy": "midway-bin deploy", + "start": "NODE_ENV=production node ./bootstrap.js", + "online": "NODE_ENV=production node ./bootstrap.js", + "online:preview": "NODE_ENV=preview node ./bootstrap.js", + "dev": "cross-env NODE_ENV=local midway-bin dev --ts --watchFile='../../core/pipeline/src,../../plugins/'", + "dev:preview": "cross-env NODE_ENV=preview midway-bin dev --ts", + "dev:syncdb": "cross-env NODE_ENV=syncdb midway-bin dev --ts --watchFile='../../core/pipeline/src'", + "test": "midway-bin test --ts", + "cov": "midway-bin cov --ts", + "lint": "mwts check", + "lint:fix": "mwts fix", + "ci": "npm run cov", + "build": "midway-bin build -c", + "build:preview": "cross-env NODE_ENV=preview midway-bin build -c", + "check": "luckyeye", + "mig": "typeorm migration:create -n name" + }, + "dependencies": { + "@alicloud/pop-core": "^1.7.12", + "@certd/pipeline": "^0.3.0", + "@certd/plugin-all": "^0.3.0", + "@koa/cors": "^3.4.3", + "@midwayjs/bootstrap": "^3.9.1", + "@midwayjs/cache": "^3.9.0", + "@midwayjs/core": "^3.9.0", + "@midwayjs/decorator": "^3.9.0", + "@midwayjs/koa": "^3.9.0", + "@midwayjs/logger": "^2.17.0", + "@midwayjs/typeorm": "^3.9.5", + "@midwayjs/validate": "^3.9.0", + "@midwayjs/i18n": "^3.9.0", + "@types/cache-manager": "^3.4.3", + "cache-manager": "^3.6.3", + "dayjs": "^1.11.7", + "glob": "^7.2.3", + "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.21", + "log4js": "^6.7.1", + "md5": "^2.3.0", + "midway-flyway-js": "^3.0.0", + "node-cron": "^3.0.2", + "sqlite3": "^5.1.4", + "svg-captcha": "^1.4.0", + "typeorm": "^0.3.11" + }, + "devDependencies": { + "@midwayjs/cli": "^1.3.21", + "@midwayjs/luckyeye": "^1.1.0", + "@midwayjs/mock": "^3.9.0", + "@midwayjs/mwcc": "^0.8.0", + "@types/jest": "^26.0.24", + "@types/koa": "2.13.4", + "@types/node": "^14.18.35", + "cross-env": "^6.0.3", + "jest": "^26.6.3", + "mwts": "^1.3.0", + "ts-jest": "^26.5.6", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.1.1", + "typescript": "^4.9.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "midway-bin-clean": [ + ".vscode/.tsbuildinfo", + "dist" + ], + "midway-luckyeye": { + "packages": [ + "midway_v2" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/fast-crud/fast-server-js" + }, + "author": "Greper", + "license": "MIT" +} diff --git a/packages/ui/certd-server/src/basic/base-controller.ts b/packages/ui/certd-server/src/basic/base-controller.ts new file mode 100644 index 000000000..38a5d76b2 --- /dev/null +++ b/packages/ui/certd-server/src/basic/base-controller.ts @@ -0,0 +1,33 @@ +import { Inject } from '@midwayjs/decorator'; +import { Context } from '@midwayjs/koa'; +import { Constants } from './constants'; + +export abstract class BaseController { + @Inject() + ctx: Context; + + /** + * 成功返回 + * @param data 返回数据 + */ + ok(data) { + const res = { + ...Constants.res.success, + data: undefined, + }; + if (data) { + res.data = data; + } + return res; + } + /** + * 失败返回 + * @param message + */ + fail(msg, code) { + return { + code: code ? code : Constants.res.error.code, + msg: msg ? msg : Constants.res.error.code, + }; + } +} diff --git a/packages/ui/certd-server/src/basic/base-service.ts b/packages/ui/certd-server/src/basic/base-service.ts new file mode 100644 index 000000000..c6b055c90 --- /dev/null +++ b/packages/ui/certd-server/src/basic/base-service.ts @@ -0,0 +1,207 @@ +import { Inject } from '@midwayjs/decorator'; +import { ValidateException } from './exception/validation-exception'; +import * as _ from 'lodash'; +import { Context } from '@midwayjs/koa'; +import { PermissionException } from './exception/permission-exception'; +import { Repository } from 'typeorm'; + +/** + * 服务基类 + */ +export abstract class BaseService { + @Inject() + ctx: Context; + + abstract getRepository(): Repository; + + /** + * 获得单个ID + * @param id ID + * @param infoIgnoreProperty 忽略返回属性 + */ + async info(id, infoIgnoreProperty?): Promise { + if (!id) { + throw new ValidateException('id不能为空'); + } + // @ts-ignore + const info = await this.getRepository().findOne({ where: { id } }); + if (info && infoIgnoreProperty) { + for (const property of infoIgnoreProperty) { + delete info[property]; + } + } + return info; + } + + /** + * 非分页查询 + * @param option 查询配置 + */ + async find(options) { + return await this.getRepository().find(options); + } + + /** + * 删除 + * @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3 + */ + async delete(ids) { + if (ids instanceof Array) { + await this.getRepository().delete(ids); + } else if (typeof ids === 'string') { + await this.getRepository().delete(ids.split(',')); + } else { + //ids是一个condition + await this.getRepository().delete(ids); + } + await this.modifyAfter(ids); + } + + /** + * 新增|修改 + * @param param 数据 + */ + async addOrUpdate(param) { + await this.getRepository().save(param); + } + + /** + * 新增 + * @param param 数据 + */ + async add(param) { + const now = new Date().getTime(); + param.createTime = now; + param.updateTime = now; + await this.addOrUpdate(param); + await this.modifyAfter(param); + return { + id: param.id, + }; + } + + /** + * 修改 + * @param param 数据 + */ + async update(param) { + if (!param.id) throw new ValidateException('no id'); + param.updateTime = new Date().getTime(); + await this.addOrUpdate(param); + await this.modifyAfter(param); + } + + /** + * 新增|修改|删除 之后的操作 + * @param data 对应数据 + */ + async modifyAfter(data) {} + + /** + * 分页查询 + * @param query 查询条件 bean + * @param page + * @param order + * @param buildQuery + */ + async page(query, page = { offset: 0, limit: 20 }, order, buildQuery) { + if (page.offset == null) { + page.offset = 0; + } + if (page.limit == null) { + page.limit = 20; + } + const qb = this.getRepository().createQueryBuilder('main'); + if (order && order.prop) { + qb.orderBy('main.' + order.prop, order.asc ? 'ASC' : 'DESC'); + } else { + qb.orderBy('id', 'DESC'); + } + qb.offset(page.offset).limit(page.limit); + //根据bean query + if (query) { + let whereSql = ''; + let index = 0; + _.forEach(query, (value, key) => { + if (!value) { + return; + } + if (index !== 0) { + whereSql += ' and '; + } + whereSql += ` main.${key} = :${key} `; + index++; + }); + if (index > 0) { + qb.where(whereSql, query); + } + } + //自定义query + if (buildQuery) { + buildQuery(qb); + } + const list = await qb.getMany(); + const total = await qb.getCount(); + return { + records: list, + total, + offset: page.offset, + limit: page.limit, + }; + } + + /** + * 分页查询 + * @param query 查询条件 bean + * @param order + * @param buildQuery + */ + async list(query, order, buildQuery) { + const qb = this.getRepository().createQueryBuilder('main'); + if (order && order.prop) { + qb.orderBy('main.' + order.prop, order.asc ? 'ASC' : 'DESC'); + } else { + qb.orderBy('id', 'DESC'); + } + //根据bean query + if (query) { + let whereSql = ''; + let index = 0; + _.forEach(query, (value, key) => { + if (!value) { + return; + } + if (index !== 0) { + whereSql += ' and '; + } + whereSql += ` main.${key} = :${key} `; + index++; + }); + if (index > 0) { + qb.where(whereSql, query); + } + } + //自定义query + if (buildQuery) { + buildQuery(qb); + } + return await qb.getMany(); + } + + async checkUserId(id = 0, userId, userKey = 'userId') { + // @ts-ignore + const res = await this.getRepository().findOne({ + // @ts-ignore + select: { [userKey]: true }, + where: { + // @ts-ignore + id, + }, + }); + // @ts-ignore + if (!res || res.userId === userId) { + return; + } + throw new PermissionException('权限不足'); + } +} diff --git a/packages/ui/certd-server/src/basic/constants.ts b/packages/ui/certd-server/src/basic/constants.ts new file mode 100644 index 000000000..992f13af0 --- /dev/null +++ b/packages/ui/certd-server/src/basic/constants.ts @@ -0,0 +1,28 @@ +export const Constants = { + res: { + error: { + code: 1, + message: 'error', + }, + success: { + code: 0, + message: 'success', + }, + validation: { + code: 10, + message: '参数错误', + }, + auth: { + code: 401, + message: '您还未登录或token已过期', + }, + permission: { + code: 402, + message: '您没有权限', + }, + preview: { + code: 10001, + message: '对不起,预览环境不允许修改此数据', + }, + }, +}; diff --git a/packages/ui/certd-server/src/basic/crud-controller.ts b/packages/ui/certd-server/src/basic/crud-controller.ts new file mode 100644 index 000000000..131cba5c7 --- /dev/null +++ b/packages/ui/certd-server/src/basic/crud-controller.ts @@ -0,0 +1,64 @@ +import { ALL, Body, Post, Query } from '@midwayjs/decorator'; +import { BaseController } from './base-controller'; + +export abstract class CrudController extends BaseController { + abstract getService(); + + @Post('/page') + async page( + @Body(ALL) + body + ) { + const pageRet = await this.getService().page( + body?.query, + body?.page, + body?.sort, + null + ); + return this.ok(pageRet); + } + + @Post('/list') + async list( + @Body(ALL) + body + ) { + const listRet = await this.getService().list(body, null, null); + return this.ok(listRet); + } + + @Post('/add') + async add( + @Body(ALL) + bean + ) { + const id = await this.getService().add(bean); + return this.ok(id); + } + + @Post('/info') + async info( + @Query('id') + id + ) { + const bean = await this.getService().info(id); + return this.ok(bean); + } + + @Post('/update') + async update( + @Body(ALL) + bean + ) { + await this.getService().update(bean); + return this.ok(null); + } + @Post('/delete') + async delete( + @Query('id') + id + ) { + await this.getService().delete([id]); + return this.ok(null); + } +} diff --git a/packages/ui/certd-server/src/basic/enum-item.ts b/packages/ui/certd-server/src/basic/enum-item.ts new file mode 100644 index 000000000..a7e4a4a88 --- /dev/null +++ b/packages/ui/certd-server/src/basic/enum-item.ts @@ -0,0 +1,11 @@ +export class EnumItem { + value: string; + label: string; + color: string; + + constructor(value, label, color) { + this.value = value; + this.label = label; + this.color = color; + } +} diff --git a/packages/ui/certd-server/src/basic/exception/auth-exception.ts b/packages/ui/certd-server/src/basic/exception/auth-exception.ts new file mode 100644 index 000000000..92e82a4d7 --- /dev/null +++ b/packages/ui/certd-server/src/basic/exception/auth-exception.ts @@ -0,0 +1,14 @@ +import { Constants } from '../constants'; +import { BaseException } from './base-exception'; +/** + * 授权异常 + */ +export class AuthException extends BaseException { + constructor(message) { + super( + 'AuthException', + Constants.res.auth.code, + message ? message : Constants.res.auth.message + ); + } +} diff --git a/packages/ui/certd-server/src/basic/exception/base-exception.ts b/packages/ui/certd-server/src/basic/exception/base-exception.ts new file mode 100644 index 000000000..a4e89604b --- /dev/null +++ b/packages/ui/certd-server/src/basic/exception/base-exception.ts @@ -0,0 +1,11 @@ +/** + * 异常基类 + */ +export class BaseException extends Error { + status: number; + constructor(name, code, message) { + super(message); + this.name = name; + this.status = code; + } +} diff --git a/packages/ui/certd-server/src/basic/exception/common-exception.ts b/packages/ui/certd-server/src/basic/exception/common-exception.ts new file mode 100644 index 000000000..f4afa91e6 --- /dev/null +++ b/packages/ui/certd-server/src/basic/exception/common-exception.ts @@ -0,0 +1,14 @@ +import { Constants } from '../constants'; +import { BaseException } from './base-exception'; +/** + * 通用异常 + */ +export class CommonException extends BaseException { + constructor(message) { + super( + 'CommonException', + Constants.res.error.code, + message ? message : Constants.res.error.message + ); + } +} diff --git a/packages/ui/certd-server/src/basic/exception/permission-exception.ts b/packages/ui/certd-server/src/basic/exception/permission-exception.ts new file mode 100644 index 000000000..edeb2b7e2 --- /dev/null +++ b/packages/ui/certd-server/src/basic/exception/permission-exception.ts @@ -0,0 +1,14 @@ +import { Constants } from '../constants'; +import { BaseException } from './base-exception'; +/** + * 授权异常 + */ +export class PermissionException extends BaseException { + constructor(message) { + super( + 'PermissionException', + Constants.res.permission.code, + message ? message : Constants.res.permission.message + ); + } +} diff --git a/packages/ui/certd-server/src/basic/exception/preview-exception.ts b/packages/ui/certd-server/src/basic/exception/preview-exception.ts new file mode 100644 index 000000000..2abfc48e9 --- /dev/null +++ b/packages/ui/certd-server/src/basic/exception/preview-exception.ts @@ -0,0 +1,14 @@ +import { Constants } from '../constants'; +import { BaseException } from './base-exception'; +/** + * 预览模式 + */ +export class PreviewException extends BaseException { + constructor(message) { + super( + 'PreviewException', + Constants.res.preview.code, + message ? message : Constants.res.preview.message + ); + } +} diff --git a/packages/ui/certd-server/src/basic/exception/validation-exception.ts b/packages/ui/certd-server/src/basic/exception/validation-exception.ts new file mode 100644 index 000000000..d595ff71b --- /dev/null +++ b/packages/ui/certd-server/src/basic/exception/validation-exception.ts @@ -0,0 +1,14 @@ +import { Constants } from '../constants'; +import { BaseException } from './base-exception'; +/** + * 校验异常 + */ +export class ValidateException extends BaseException { + constructor(message) { + super( + 'ValidateException', + Constants.res.validation.code, + message ? message : Constants.res.validation.message + ); + } +} diff --git a/packages/ui/certd-server/src/basic/result.ts b/packages/ui/certd-server/src/basic/result.ts new file mode 100644 index 000000000..ea1022444 --- /dev/null +++ b/packages/ui/certd-server/src/basic/result.ts @@ -0,0 +1,18 @@ +export class Result { + code: number; + msg: string; + data: T; + constructor(code, msg, data?) { + this.code = code; + this.msg = msg; + this.data = data; + } + + static error(code = 1, msg) { + return new Result(code, msg, null); + } + + static success(msg, data?) { + return new Result(0, msg, data); + } +} diff --git a/packages/ui/certd-server/src/config/config.default.ts b/packages/ui/certd-server/src/config/config.default.ts new file mode 100644 index 000000000..ed2d93628 --- /dev/null +++ b/packages/ui/certd-server/src/config/config.default.ts @@ -0,0 +1,60 @@ +import { join } from 'path'; +import { FlywayHistory } from 'midway-flyway-js/dist/entity'; + +import { MidwayConfig } from '@midwayjs/core'; +import { UserEntity } from '../modules/authority/entity/user'; + +export default { + // use for cookie sign key, should change to your own and keep security + keys: 'certd666', + koa: { + port: 7001, + }, + cron: {}, + /** + * 演示环境 + */ + preview: { + enabled: false, + }, + + /** + * 数据库 + */ + typeorm: { + dataSource: { + default: { + /** + * 单数据库实例 + */ + type: 'sqlite', + database: join(__dirname, '../../data/db.sqlite'), + synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true + logging: true, + + // 配置实体模型 或者 entities: '/entity', + entities: [ + '**/modules/*/entity/*.ts', + FlywayHistory, + UserEntity, + ], + }, + }, + }, + /** + * 自动升级数据库脚本 + */ + flyway: { + scriptDir: join(__dirname, '../../db/migration'), + }, + + biz: { + jwt: { + secret: 'greper-is-666', + expire: 7 * 24 * 60, //单位秒 + }, + auth: { + ignoreUrls: ['/', '/api/login', '/api/register'], + }, + }, +} as MidwayConfig; diff --git a/packages/ui/certd-server/src/config/config.preview.ts b/packages/ui/certd-server/src/config/config.preview.ts new file mode 100644 index 000000000..aa4bc6aac --- /dev/null +++ b/packages/ui/certd-server/src/config/config.preview.ts @@ -0,0 +1,10 @@ +import { MidwayConfig } from '@midwayjs/core'; + +export default { + /** + * 演示环境 + */ + preview: { + enabled: true, + } +} as MidwayConfig; diff --git a/packages/ui/certd-server/src/config/config.production.ts b/packages/ui/certd-server/src/config/config.production.ts new file mode 100644 index 000000000..aa4bc6aac --- /dev/null +++ b/packages/ui/certd-server/src/config/config.production.ts @@ -0,0 +1,10 @@ +import { MidwayConfig } from '@midwayjs/core'; + +export default { + /** + * 演示环境 + */ + preview: { + enabled: true, + } +} as MidwayConfig; diff --git a/packages/ui/certd-server/src/config/config.syncdb.ts b/packages/ui/certd-server/src/config/config.syncdb.ts new file mode 100644 index 000000000..478af50b6 --- /dev/null +++ b/packages/ui/certd-server/src/config/config.syncdb.ts @@ -0,0 +1,11 @@ +import { MidwayConfig } from '@midwayjs/core'; + +export default { + typeorm: { + dataSource: { + default: { + synchronize: true, // 如果第一次使用,不存在表,有同步的需求可以写 true + }, + }, + }, +} as MidwayConfig; diff --git a/packages/ui/certd-server/src/configuration.ts b/packages/ui/certd-server/src/configuration.ts new file mode 100644 index 000000000..97970bd52 --- /dev/null +++ b/packages/ui/certd-server/src/configuration.ts @@ -0,0 +1,60 @@ +import * as validateComp from '@midwayjs/validate'; +import * as productionConfig from './config/config.production'; +import * as previewConfig from './config/config.preview'; +import * as defaultConfig from './config/config.default'; +import { Configuration, App } from '@midwayjs/decorator'; +import * as koa from '@midwayjs/koa'; +import * as orm from '@midwayjs/typeorm'; +import * as cache from '@midwayjs/cache'; +import cors from '@koa/cors'; +import { join } from 'path'; +import * as flyway from 'midway-flyway-js'; +import { ReportMiddleware } from './middleware/report'; +import { GlobalExceptionMiddleware } from './middleware/global-exception'; +import { PreviewMiddleware } from './middleware/preview'; +import { AuthorityMiddleware } from './middleware/authority'; +import * as pipeline from './plugins/pipeline'; +import * as cron from './plugins/cron'; +@Configuration({ + imports: [koa, orm, cache, flyway, validateComp,pipeline, cron], + importConfigs: [ + { + default: defaultConfig, + preview: previewConfig, + production: productionConfig, + }, + ], +}) +export class ContainerConfiguration {} +@Configuration({ + conflictCheck: true, + importConfigs: [join(__dirname, './config')], +}) +export class ContainerLifeCycle { + @App() + app: koa.Application; + + async onReady() { + //跨域 + this.app.use( + cors({ + origin: '*', + }) + ); + // bodyparser options see https://github.com/koajs/bodyparser + //this.app.use(bodyParser()); + //请求日志打印 + + this.app.useMiddleware([ + ReportMiddleware, + //统一异常处理 + GlobalExceptionMiddleware, + //预览模式限制修改id<1000的数据 + PreviewMiddleware, + //授权处理 + AuthorityMiddleware, + ]); + + //加载插件 + } +} diff --git a/packages/ui/certd-server/src/controller/home.ts b/packages/ui/certd-server/src/controller/home.ts new file mode 100644 index 000000000..6aa5a9a74 --- /dev/null +++ b/packages/ui/certd-server/src/controller/home.ts @@ -0,0 +1,10 @@ +import { Controller, Get, Provide } from '@midwayjs/decorator'; + +@Provide() +@Controller('/') +export class HomeController { + @Get('/') + async home(): Promise { + return 'Hello Midwayjs!'; + } +} diff --git a/packages/ui/certd-server/src/middleware/authority.ts b/packages/ui/certd-server/src/middleware/authority.ts new file mode 100644 index 000000000..431d2b692 --- /dev/null +++ b/packages/ui/certd-server/src/middleware/authority.ts @@ -0,0 +1,48 @@ +import { Config, Provide } from '@midwayjs/decorator'; +import { + IWebMiddleware, + IMidwayKoaContext, + IMidwayKoaNext, +} from '@midwayjs/koa'; +import * as _ from 'lodash'; +import * as jwt from 'jsonwebtoken'; +import { Constants } from '../basic/constants'; + +/** + * 权限校验 + */ +@Provide() +export class AuthorityMiddleware implements IWebMiddleware { + @Config('biz.jwt.secret') + private secret: string; + @Config('biz.auth.ignoreUrls') + private ignoreUrls: string[]; + + resolve() { + return async (ctx: IMidwayKoaContext, next: IMidwayKoaNext) => { + const { url } = ctx; + const token = ctx.get('Authorization'); + // 路由地址为 admin前缀的 需要权限校验 + // console.log('ctx', ctx); + const queryIndex = url.indexOf('?'); + let uri = url; + if (queryIndex >= 0) { + uri = url.substring(0, queryIndex); + } + const yes = this.ignoreUrls.includes(uri); + if (yes) { + await next(); + return; + } + + try { + ctx.user = jwt.verify(token, this.secret); + } catch (err) { + ctx.status = 401; + ctx.body = Constants.res.auth; + return; + } + await next(); + }; + } +} diff --git a/packages/ui/certd-server/src/middleware/global-exception.ts b/packages/ui/certd-server/src/middleware/global-exception.ts new file mode 100644 index 000000000..b67faf118 --- /dev/null +++ b/packages/ui/certd-server/src/middleware/global-exception.ts @@ -0,0 +1,27 @@ +import { Provide } from '@midwayjs/decorator'; +import { + IWebMiddleware, + IMidwayKoaContext, + IMidwayKoaNext, +} from '@midwayjs/koa'; +import { logger } from '../utils/logger'; +import { Result } from '../basic/result'; + +@Provide() +export class GlobalExceptionMiddleware implements IWebMiddleware { + resolve() { + return async (ctx: IMidwayKoaContext, next: IMidwayKoaNext) => { + const { url } = ctx; + const startTime = Date.now(); + logger.info('请求开始:', url); + try { + await next(); + logger.info('请求完成', url, Date.now() - startTime + 'ms'); + } catch (err) { + logger.error('请求异常:', url, Date.now() - startTime + 'ms', err); + ctx.status = 200; + ctx.body = Result.error(err.code != null ? err.code : 1, err.message); + } + }; + } +} diff --git a/packages/ui/certd-server/src/middleware/preview.ts b/packages/ui/certd-server/src/middleware/preview.ts new file mode 100644 index 000000000..c93ecfa7b --- /dev/null +++ b/packages/ui/certd-server/src/middleware/preview.ts @@ -0,0 +1,50 @@ +import { Config, Provide } from '@midwayjs/decorator'; +import { + IMidwayKoaContext, + IMidwayKoaNext, + IWebMiddleware, +} from '@midwayjs/koa'; +import { PreviewException } from '../basic/exception/preview-exception'; + +/** + * 预览模式 + */ +@Provide() +export class PreviewMiddleware implements IWebMiddleware { + @Config('preview.enabled') + private preview: boolean; + + resolve() { + return async (ctx: IMidwayKoaContext, next: IMidwayKoaNext) => { + if (!this.preview) { + await next(); + return; + } + let { url, request } = ctx; + const body: any = request.body; + let id = body.id || request.query.id; + const roleId = body.roleId; + if (id == null && roleId != null) { + id = roleId; + } + if (id != null && typeof id === 'string') { + id = parseInt(id); + } + if (url.indexOf('?') !== -1) { + url = url.substring(0, url.indexOf('?')); + } + const isModify = + url.endsWith('update') || + url.endsWith('delete') || + url.endsWith('authz'); + const isPreviewId = id < 1000; + if (this.preview && isModify && isPreviewId) { + throw new PreviewException( + '对不起,预览环境不允许修改此数据,如需体验请添加新数据' + ); + } + await next(); + return; + }; + } +} diff --git a/packages/ui/certd-server/src/middleware/report.ts b/packages/ui/certd-server/src/middleware/report.ts new file mode 100644 index 000000000..d81eff85d --- /dev/null +++ b/packages/ui/certd-server/src/middleware/report.ts @@ -0,0 +1,28 @@ +import { Provide } from '@midwayjs/decorator'; +import { + IWebMiddleware, + IMidwayKoaContext, + IMidwayKoaNext, +} from '@midwayjs/koa'; +import { logger } from '../utils/logger'; + +@Provide() +export class ReportMiddleware implements IWebMiddleware { + resolve() { + return async (ctx: IMidwayKoaContext, next: IMidwayKoaNext) => { + const { url } = ctx; + logger.info('请求开始:', url); + const startTime = Date.now(); + await next(); + if (ctx.status !== 200) { + logger.error( + '请求失败:', + url, + ctx.status, + Date.now() - startTime + 'ms' + ); + } + logger.info('请求完成:', url, ctx.status, Date.now() - startTime + 'ms'); + }; + } +} diff --git a/packages/ui/certd-server/src/modules/authority/controller/permission-controller.ts b/packages/ui/certd-server/src/modules/authority/controller/permission-controller.ts new file mode 100644 index 000000000..6e0f0e176 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/controller/permission-controller.ts @@ -0,0 +1,62 @@ +import { + ALL, + Body, + Controller, + Inject, + Post, + Provide, + Query, +} from '@midwayjs/decorator'; +import { CrudController } from '../../../basic/crud-controller'; +import { PermissionService } from '../service/permission-service'; + +/** + * 权限资源 + */ +@Provide() +@Controller('/api/sys/authority/permission') +export class PermissionController extends CrudController { + @Inject() + service: PermissionService; + + getService() { + return this.service; + } + + @Post('/page') + async page( + @Body(ALL) + body + ) { + return await super.page(body); + } + + @Post('/add') + async add( + @Body(ALL) + bean + ) { + return await super.add(bean); + } + + @Post('/update') + async update( + @Body(ALL) + bean + ) { + return await super.update(bean); + } + @Post('/delete') + async delete( + @Query('id') + id + ) { + return await super.delete(id); + } + + @Post('/tree') + async tree() { + const tree = await this.service.tree({}); + return this.ok(tree); + } +} diff --git a/packages/ui/certd-server/src/modules/authority/controller/role-controller.ts b/packages/ui/certd-server/src/modules/authority/controller/role-controller.ts new file mode 100644 index 000000000..3c3a99199 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/controller/role-controller.ts @@ -0,0 +1,95 @@ +import { + ALL, + Body, + Controller, + Inject, + Post, + Provide, + Query, +} from '@midwayjs/decorator'; +import { CrudController } from '../../../basic/crud-controller'; +import { RoleService } from '../service/role-service'; + +/** + * 系统用户 + */ +@Provide() +@Controller('/api/sys/authority/role') +export class RoleController extends CrudController { + @Inject() + service: RoleService; + + getService() { + return this.service; + } + + @Post('/page') + async page( + @Body(ALL) + body + ) { + return await super.page(body); + } + + @Post('/list') + async list() { + const ret = await this.service.find({}); + return this.ok(ret); + } + + @Post('/add') + async add( + @Body(ALL) + bean + ) { + return await super.add(bean); + } + + @Post('/update') + async update( + @Body(ALL) + bean + ) { + return await super.update(bean); + } + @Post('/delete') + async delete( + @Query('id') + id + ) { + return await super.delete(id); + } + + @Post('/getPermissionTree') + async getPermissionTree( + @Query('id') + id + ) { + const ret = await this.service.getPermissionTreeByRoleId(id); + return this.ok(ret); + } + + @Post('/getPermissionIds') + async getPermissionIds( + @Query('id') + id + ) { + const ret = await this.service.getPermissionIdsByRoleId(id); + return this.ok(ret); + } + + /** + * 给角色授予权限 + * @param id + */ + @Post('/authz') + async authz( + @Body('roleId') + roleId, + @Body('permissionIds') + permissionIds + ) { + await this.service.authz(roleId, permissionIds); + return this.ok(null); + } +} diff --git a/packages/ui/certd-server/src/modules/authority/controller/user-controller.ts b/packages/ui/certd-server/src/modules/authority/controller/user-controller.ts new file mode 100644 index 000000000..1b8096240 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/controller/user-controller.ts @@ -0,0 +1,118 @@ +import { + Provide, + Controller, + Post, + Inject, + Body, + Query, + ALL, +} from '@midwayjs/decorator'; +import { UserService } from '../service/user-service'; +import { CrudController } from '../../../basic/crud-controller'; +import { RoleService } from '../service/role-service'; +import { PermissionService } from '../service/permission-service'; + +/** + * 系统用户 + */ +@Provide() +@Controller('/api/sys/authority/user') +export class UserController extends CrudController { + @Inject() + service: UserService; + + @Inject() + roleService: RoleService; + @Inject() + permissionService: PermissionService; + + getService() { + return this.service; + } + + @Post('/page') + async page( + @Body(ALL) + body + ) { + const ret = await super.page(body); + + const users = ret.data.records; + + //获取roles + const userIds = users.map(item => item.id); + const userRoles = await this.roleService.getByUserIds(userIds); + const userRolesMap = new Map(); + for (const ur of userRoles) { + let roles = userRolesMap.get(ur.userId); + if (roles == null) { + roles = []; + userRolesMap.set(ur.userId, roles); + } + roles.push(ur.roleId); + } + + for (const record of users) { + //withRoles + record.roles = userRolesMap.get(record.id); + //删除密码字段 + delete record.password; + } + + return ret; + } + + @Post('/add') + async add( + @Body(ALL) + bean + ) { + return await super.add(bean); + } + + @Post('/update') + async update( + @Body(ALL) + bean + ) { + return await super.update(bean); + } + @Post('/delete') + async delete( + @Query('id') + id + ) { + return await super.delete(id); + } + + /** + * 当前登录用户的个人信息 + */ + @Post('/mine') + public async mine() { + const id = this.ctx.user.id; + const info = await this.service.info(id, ['password']); + return this.ok(info); + } + + /** + * 当前登录用户的权限列表 + */ + @Post('/permissions') + public async permissions() { + const id = this.ctx.user.id; + const permissions = await this.service.getUserPermissions(id); + return this.ok(permissions); + } + + /** + * 当前登录用户的权限树形列表 + */ + @Post('/permissionTree') + public async permissionTree() { + const id = this.ctx.user.id; + const permissions = await this.service.getUserPermissions(id); + const tree = this.permissionService.buildTree(permissions); + return this.ok(tree); + } +} diff --git a/packages/ui/certd-server/src/modules/authority/entity/permission.ts b/packages/ui/certd-server/src/modules/authority/entity/permission.ts new file mode 100644 index 000000000..6fa91e18d --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/entity/permission.ts @@ -0,0 +1,40 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +/** + * 权限 + */ +@Entity('sys_permission') +export class PermissionEntity { + @PrimaryGeneratedColumn() + id: number; + @Column({ comment: '标题', length: 100 }) + title: string; + /** + * 权限代码 + * 示例:sys:user:read + */ + @Column({ comment: '权限代码', length: 100, nullable: true }) + permission: string; + + @Column({ name: 'parent_id', comment: '父节点ID', default: -1 }) + parentId: number; + + @Column({ comment: '排序号' }) + sort: number; + + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; + + // @ManyToMany(type => RoleEntity, res => res.permissions) + // roles: RoleEntity[]; +} diff --git a/packages/ui/certd-server/src/modules/authority/entity/role-permission.ts b/packages/ui/certd-server/src/modules/authority/entity/role-permission.ts new file mode 100644 index 000000000..85ac0c734 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/entity/role-permission.ts @@ -0,0 +1,12 @@ +import { Entity, PrimaryColumn } from 'typeorm'; + +/** + * 角色权限多对多 + */ +@Entity('sys_role_permission') +export class RolePermissionEntity { + @PrimaryColumn({ name: 'role_id' }) + roleId: number; + @PrimaryColumn({ name: 'permission_id' }) + permissionId: number; +} diff --git a/packages/ui/certd-server/src/modules/authority/entity/role.ts b/packages/ui/certd-server/src/modules/authority/entity/role.ts new file mode 100644 index 000000000..2aecfb743 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/entity/role.ts @@ -0,0 +1,43 @@ +import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; + +/** + * 角色 + */ +@Entity('sys_role') +export class RoleEntity { + @PrimaryGeneratedColumn() + id: number; + @Index({ unique: true }) + @Column({ comment: '角色名称', length: 100 }) + name: string; + + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; + + // @ManyToMany(type => PermissionEntity, res => res.roles) + // @JoinTable({ + // name: 'sys_role_resources', + // joinColumn: { + // name: 'roleId', + // referencedColumnName: 'id', + // }, + // inverseJoinColumn: { + // name: 'resourceId', + // referencedColumnName: 'id', + // }, + // }) + // resources: PermissionEntity[]; + + // @ManyToMany(type => UserEntity, res => res.roles) + // users: UserEntity[]; +} diff --git a/packages/ui/certd-server/src/modules/authority/entity/user-role.ts b/packages/ui/certd-server/src/modules/authority/entity/user-role.ts new file mode 100644 index 000000000..533bf6441 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/entity/user-role.ts @@ -0,0 +1,12 @@ +import { Entity, PrimaryColumn } from 'typeorm'; + +/** + * 用户角色多对多 + */ +@Entity('sys_user_role') +export class UserRoleEntity { + @PrimaryColumn({ name: 'role_id' }) + roleId: number; + @PrimaryColumn({ name: 'user_id' }) + userId: number; +} diff --git a/packages/ui/certd-server/src/modules/authority/entity/user.ts b/packages/ui/certd-server/src/modules/authority/entity/user.ts new file mode 100644 index 000000000..598245836 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/entity/user.ts @@ -0,0 +1,63 @@ +import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; + +/** + * 系统用户 + */ +@Entity('sys_user') +export class UserEntity { + @PrimaryGeneratedColumn() + id: number; + @Index({ unique: true }) + @Column({ comment: '用户名', length: 100 }) + username: string; + + @Column({ comment: '密码', length: 100 }) + password: string; + + @Column({ name: 'nick_name', comment: '昵称', length: 100, nullable: true }) + nickName: string; + + @Column({ comment: '头像', length: 255, nullable: true }) + avatar: string; + + @Column({ name: 'phone_code', comment: '区号', length: 20, nullable: true }) + phoneCode: string; + + @Column({ comment: '手机', length: 20, nullable: true }) + mobile: string; + + @Column({ comment: '邮箱', length: 50, nullable: true }) + email: string; + + @Column({ comment: '备注', length: 100, nullable: true }) + remark: string; + + @Column({ comment: '状态 0:禁用 1:启用', default: 1, type: 'int' }) + status: number; + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; + + // @ManyToMany(type => RoleEntity, res => res.users) + // @JoinTable({ + // name: 'sys_user_roles', + // joinColumn: { + // name: 'userId', + // referencedColumnName: 'id', + // }, + // inverseJoinColumn: { + // name: 'roleId', + // referencedColumnName: 'id', + // }, + // }) + // roles: RoleEntity[]; +} diff --git a/packages/ui/certd-server/src/modules/authority/enums/ResourceTypeEnum.ts b/packages/ui/certd-server/src/modules/authority/enums/ResourceTypeEnum.ts new file mode 100644 index 000000000..cf3411ece --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/enums/ResourceTypeEnum.ts @@ -0,0 +1,17 @@ +import { EnumItem } from '../../../basic/enum-item'; +import * as _ from 'lodash'; +class ResourceTypes { + MENU = new EnumItem('menu', '菜单', 'blue'); + BTN = new EnumItem('btn', '按钮', 'green'); + ROUTE = new EnumItem('route', '路由', 'red'); + + names() { + const list = []; + _.forEach(this, (item, key) => { + list.push(item); + }); + return list; + } +} + +export const ResourceTypeEnum = new ResourceTypes(); diff --git a/packages/ui/certd-server/src/modules/authority/service/permission-service.ts b/packages/ui/certd-server/src/modules/authority/service/permission-service.ts new file mode 100644 index 000000000..950a04973 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/service/permission-service.ts @@ -0,0 +1,52 @@ +import { Provide } from '@midwayjs/decorator'; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { PermissionEntity } from '../entity/permission'; + +/** + * 权限资源 + */ +@Provide() +export class PermissionService extends BaseService { + @InjectEntityModel(PermissionEntity) + repository: Repository; + + getRepository() { + return this.repository; + } + + async tree(options: any = {}) { + if (options.order == null) { + options.order = { + sort: 'ASC', + }; + } + const list = await this.find(options); + return this.buildTree(list); + } + + buildTree(list: any) { + const idMap = {}; + const root = []; + for (const item of list) { + idMap[item.id] = item; + if (item.parentId == null || item.parentId <= 0) { + root.push(item); + } + } + + for (const item of list) { + if (item.parentId > 0) { + const parent = idMap[item.parentId]; + if (parent) { + if (parent.children == null) { + parent.children = []; + } + parent.children.push(item); + } + } + } + return root; + } +} diff --git a/packages/ui/certd-server/src/modules/authority/service/role-permission-service.ts b/packages/ui/certd-server/src/modules/authority/service/role-permission-service.ts new file mode 100644 index 000000000..e2b2b3d4c --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/service/role-permission-service.ts @@ -0,0 +1,18 @@ +import { Provide } from '@midwayjs/decorator'; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { RolePermissionEntity } from '../entity/role-permission'; + +/** + * 角色->权限 + */ +@Provide() +export class RolePermissionService extends BaseService { + @InjectEntityModel(RolePermissionEntity) + repository: Repository; + + getRepository() { + return this.repository; + } +} diff --git a/packages/ui/certd-server/src/modules/authority/service/role-service.ts b/packages/ui/certd-server/src/modules/authority/service/role-service.ts new file mode 100644 index 000000000..330bc360a --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/service/role-service.ts @@ -0,0 +1,101 @@ +import { Inject, Provide } from '@midwayjs/decorator'; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { In, Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { RoleEntity } from '../entity/role'; +import { UserRoleService } from './user-role-service'; +import { RolePermissionEntity } from '../entity/role-permission'; +import { PermissionService } from './permission-service'; +import * as _ from 'lodash'; +import { RolePermissionService } from './role-permission-service'; +/** + * 角色 + */ +@Provide() +export class RoleService extends BaseService { + @InjectEntityModel(RoleEntity) + repository: Repository; + @Inject() + userRoleService: UserRoleService; + @Inject() + permissionService: PermissionService; + @Inject() + rolePermissionService: RolePermissionService; + + getRepository() { + return this.repository; + } + + async getRoleIdsByUserId(id: any) { + const userRoles = await this.userRoleService.find({ + where: { userId: id }, + }); + return userRoles.map(item => item.roleId); + } + async getByUserIds(ids: any) { + return await this.userRoleService.find({ + where: { + userId: In(ids), + }, + }); + } + + async getPermissionByRoleIds(roleIds: any) { + return await this.permissionService.repository + .createQueryBuilder('permission') + .innerJoinAndSelect( + RolePermissionEntity, + 'rp', + 'rp.permissionId = permission.id and rp.roleId in (:...roleIds)', + { roleIds } + ) + .getMany(); + } + + async addRoles(userId: number, roles) { + if (roles == null || roles.length === 0) { + return; + } + for (const roleId of roles) { + await this.userRoleService.add({ + userId, + roleId, + }); + } + } + + async updateRoles(userId, roles) { + if (roles == null) { + return; + } + const oldRoleIds = await this.getRoleIdsByUserId(userId); + if (_.xor(roles, oldRoleIds).length === 0) { + //如果两个数组相等,则不修改 + return; + } + //先删除所有 + await this.userRoleService.delete({ userId }); + //再添加 + await this.addRoles(userId, roles); + } + + async getPermissionTreeByRoleId(id: any) { + const list = await this.getPermissionByRoleIds([id]); + return this.permissionService.buildTree(list); + } + + async getPermissionIdsByRoleId(id: any) { + const list = await this.getPermissionByRoleIds([id]); + return list.map(item => item.id); + } + + async authz(roleId: any, permissionIds: any) { + await this.rolePermissionService.delete({ roleId }); + for (const permissionId of permissionIds) { + await this.rolePermissionService.add({ + roleId, + permissionId, + }); + } + } +} diff --git a/packages/ui/certd-server/src/modules/authority/service/user-role-service.ts b/packages/ui/certd-server/src/modules/authority/service/user-role-service.ts new file mode 100644 index 000000000..7c528cff4 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/service/user-role-service.ts @@ -0,0 +1,18 @@ +import { Provide } from '@midwayjs/decorator'; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { UserRoleEntity } from '../entity/user-role'; + +/** + * 用户->角色 + */ +@Provide() +export class UserRoleService extends BaseService { + @InjectEntityModel(UserRoleEntity) + repository: Repository; + + getRepository() { + return this.repository; + } +} diff --git a/packages/ui/certd-server/src/modules/authority/service/user-service.ts b/packages/ui/certd-server/src/modules/authority/service/user-service.ts new file mode 100644 index 000000000..0fc0c5f32 --- /dev/null +++ b/packages/ui/certd-server/src/modules/authority/service/user-service.ts @@ -0,0 +1,113 @@ +import { Inject, Provide } from '@midwayjs/decorator'; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { UserEntity } from '../entity/user'; +import _ from 'lodash'; +import md5 from 'md5'; +import { CommonException } from '../../../basic/exception/common-exception'; +import { BaseService } from '../../../basic/base-service'; +import { logger } from '../../../utils/logger'; +import { RoleService } from './role-service'; +import { PermissionService } from './permission-service'; +import { UserRoleService } from './user-role-service'; + +/** + * 系统用户 + */ +@Provide() +export class UserService extends BaseService { + @InjectEntityModel(UserEntity) + repository: Repository; + @Inject() + roleService: RoleService; + @Inject() + permissionService: PermissionService; + @Inject() + userRoleService: UserRoleService; + + getRepository() { + return this.repository; + } + + /** + * 获得个人信息 + */ + async mine() { + const info = await this.repository.findOne({ + where: { + id: this.ctx.user.id, + }, + }); + delete info.password; + return info; + } + + /** + * 新增 + * @param param + */ + async add(param) { + const exists = await this.repository.findOne({ + where: { + username: param.username, + }, + }); + if (!_.isEmpty(exists)) { + throw new CommonException('用户名已经存在'); + } + const password = param.password ?? '123456'; + param.password = md5(password); // 默认密码 建议未改密码不能登陆 + await super.add(param); + //添加角色 + if (param.roles && param.roles.length > 0) { + await this.roleService.addRoles(param.id, param.roles); + } + return param.id; + } + + /** + * 修改 + * @param param 数据 + */ + async update(param) { + if (param.id == null) { + throw new CommonException('id不能为空'); + } + const userInfo = await this.repository.findOne({ + where: { id: param.id }, + }); + if (!userInfo) { + throw new CommonException('用户不存在'); + } + + delete param.username; + if (!_.isEmpty(param.password)) { + param.password = md5(param.password); + } else { + delete param.password; + } + await super.update(param); + await this.roleService.updateRoles(param.id, param.roles); + } + + async findOne(param) { + return this.repository.findOne({ + where: param, + }); + } + + checkPassword(rawPassword: any, md5Password: any) { + logger.info('md5', md5('123456')); + return md5(rawPassword) === md5Password; + } + + /** + * 获取用户的菜单资源列表 + * @param id + */ + async getUserPermissions(id: any) { + const roleIds = await this.roleService.getRoleIdsByUserId(id); + + return await this.roleService.getPermissionByRoleIds(roleIds); + } +} diff --git a/packages/ui/certd-server/src/modules/basic/controller/basic-controller.ts b/packages/ui/certd-server/src/modules/basic/controller/basic-controller.ts new file mode 100644 index 000000000..e0d505998 --- /dev/null +++ b/packages/ui/certd-server/src/modules/basic/controller/basic-controller.ts @@ -0,0 +1,55 @@ +import { Rule, RuleType } from '@midwayjs/validate'; +import { ALL, Inject } from '@midwayjs/decorator'; +import { Body } from '@midwayjs/decorator'; +import { Controller, Post, Provide } from '@midwayjs/decorator'; +import { BaseController } from '../../../basic/base-controller'; +import { CodeService } from '../service/code-service'; +export class SmsCodeReq { + @Rule(RuleType.number().required()) + phoneCode: number; + + @Rule(RuleType.string().required()) + mobile: string; + + @Rule(RuleType.string().required().max(10)) + randomStr: string; + + @Rule(RuleType.number().required().max(4)) + imgCode: string; +} + +// const enumsMap = {}; +// glob('src/modules/**/enums/*.ts', {}, (err, matches) => { +// console.log('matched', matches); +// for (const filePath of matches) { +// const module = require('/' + filePath); +// console.log('modules', module); +// } +// }); + +/** + */ +@Provide() +@Controller('/api/basic') +export class BasicController extends BaseController { + @Inject() + codeService: CodeService; + @Post('/sendSmsCode') + public sendSmsCode( + @Body(ALL) + body: SmsCodeReq + ) { + // 设置缓存内容 + return this.ok(null); + } + + @Post('/captcha') + public async getCaptcha( + @Body() + randomStr + ) { + console.assert(randomStr < 10, 'randomStr 过长'); + const captcha = await this.codeService.generateCaptcha(randomStr); + return this.ok(captcha.data); + } +} diff --git a/packages/ui/certd-server/src/modules/basic/service/code-service.ts b/packages/ui/certd-server/src/modules/basic/service/code-service.ts new file mode 100644 index 000000000..cb306af67 --- /dev/null +++ b/packages/ui/certd-server/src/modules/basic/service/code-service.ts @@ -0,0 +1,57 @@ +import { Inject, Provide } from '@midwayjs/decorator'; +import { CacheManager } from '@midwayjs/cache'; +const svgCaptcha = require('svg-captcha'); + +// {data: '', text: 'abcd'} +/** + */ +@Provide() +export class CodeService { + @Inject() + cache: CacheManager; // 依赖注入CacheManager + + /** + */ + async generateCaptcha(randomStr) { + console.assert(randomStr < 10, 'randomStr 过长'); + const c = svgCaptcha.create(); + //{data: '', text: 'abcd'} + const imgCode = c.text; // = RandomUtil.randomStr(4, true); + await this.cache.set('imgCode:' + randomStr, imgCode, { + ttl: 2 * 60 * 1000, //过期时间 2分钟 + }); + return c; + } + + async getCaptchaText(randomStr) { + return await this.cache.get('imgCode:' + randomStr); + } + + async removeCaptcha(randomStr) { + await this.cache.del('imgCode:' + randomStr); + } + + async checkCaptcha(randomStr, userCaptcha) { + const code = await this.getCaptchaText(randomStr); + if (code == null) { + throw new Error('验证码已过期'); + } + if (code !== userCaptcha) { + throw new Error('验证码不正确'); + } + return true; + } + /** + */ + async sendSms(phoneCode, mobile, smsCode) { + console.assert(phoneCode != null && mobile != null, '手机号不能为空'); + console.assert(smsCode != null, '验证码不能为空'); + } + + /** + * loginBySmsCode + */ + async loginBySmsCode(user, smsCode) { + console.assert(user.mobile != null, '手机号不能为空'); + } +} diff --git a/packages/ui/certd-server/src/modules/login/controller/login-controller.ts b/packages/ui/certd-server/src/modules/login/controller/login-controller.ts new file mode 100644 index 000000000..efe0212bc --- /dev/null +++ b/packages/ui/certd-server/src/modules/login/controller/login-controller.ts @@ -0,0 +1,30 @@ +import { + Body, + Controller, + Inject, + Post, + Provide, + ALL, +} from '@midwayjs/decorator'; +import { LoginService } from '../service/login-service'; +import { BaseController } from '../../../basic/base-controller'; + +/** + */ +@Provide() +@Controller('/api/') +export class LoginController extends BaseController { + @Inject() + loginService: LoginService; + @Post('/login') + public async login( + @Body(ALL) + user + ) { + const token = await this.loginService.login(user); + return this.ok(token); + } + + @Post('/logout') + public logout() {} +} diff --git a/packages/ui/certd-server/src/modules/login/service/login-service.ts b/packages/ui/certd-server/src/modules/login/service/login-service.ts new file mode 100644 index 000000000..85ceaee5d --- /dev/null +++ b/packages/ui/certd-server/src/modules/login/service/login-service.ts @@ -0,0 +1,52 @@ +import { Config, Inject, Provide } from '@midwayjs/decorator'; +import { UserService } from '../../authority/service/user-service'; +import * as jwt from 'jsonwebtoken'; +import { CommonException } from '../../../basic/exception/common-exception'; + +/** + * 系统用户 + */ +@Provide() +export class LoginService { + @Inject() + userService: UserService; + @Config('biz.jwt') + private jwt: any; + + /** + * login + */ + async login(user) { + console.assert(user.username != null, '用户名不能为空'); + const info = await this.userService.findOne({ username: user.username }); + if (info == null) { + throw new CommonException('用户名或密码错误'); + } + const right = this.userService.checkPassword(user.password, info.password); + if (!right) { + throw new CommonException('用户名或密码错误'); + } + + return this.generateToken(info); + } + + /** + * 生成token + * @param user 用户对象 + */ + async generateToken(user) { + const tokenInfo = { + username: user.username, + id: user.id, + }; + const expire = this.jwt.expire; + const token = jwt.sign(tokenInfo, this.jwt.secret, { + expiresIn: expire, + }); + + return { + token, + expire, + }; + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/auto/auto-register-cron.ts b/packages/ui/certd-server/src/modules/pipeline/auto/auto-register-cron.ts new file mode 100644 index 000000000..263d67090 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/auto/auto-register-cron.ts @@ -0,0 +1,26 @@ +import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/decorator"; +import { PipelineService } from '../service/pipeline-service'; +import { logger } from '../../../utils/logger'; + +@Autoload() +@Scope(ScopeEnum.Singleton) +export class AutoRegisterCron { + @Inject() + pipelineService: PipelineService; + + // @Inject() + // echoPlugin: EchoPlugin; + + @Init() + async init() { + logger.info('加载定时trigger开始'); + await this.pipelineService.onStartup(); + // logger.info(this.echoPlugin, this.echoPlugin.test); + // logger.info('加载定时trigger完成'); + // + // const meta = getClassMetadata(CLASS_KEY, this.echoPlugin); + // console.log('meta', meta); + // const metas = listPropertyDataFromClass(CLASS_KEY, this.echoPlugin); + // console.log('metas', metas); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/controller/access-controller.ts b/packages/ui/certd-server/src/modules/pipeline/controller/access-controller.ts new file mode 100644 index 000000000..529cdd7d6 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/controller/access-controller.ts @@ -0,0 +1,80 @@ +import { + ALL, + Body, + Controller, + Inject, + Post, + Provide, + Query, +} from '@midwayjs/decorator'; +import { CrudController } from '../../../basic/crud-controller'; +import { AccessService } from '../service/access-service'; + +/** + * 授权 + */ +@Provide() +@Controller('/api/pi/access') +export class AccessController extends CrudController { + @Inject() + service: AccessService; + + getService() { + return this.service; + } + + @Post('/page') + async page(@Body(ALL) body) { + body.query = body.query ?? {}; + body.query.userId = this.ctx.user.id; + return super.page(body); + } + + @Post('/list') + async list(@Body(ALL) body) { + body.userId = this.ctx.user.id; + return super.list(body); + } + + @Post('/add') + async add(@Body(ALL) bean) { + bean.userId = this.ctx.user.id; + return super.add(bean); + } + + @Post('/update') + async update(@Body(ALL) bean) { + await this.service.checkUserId(bean.id, this.ctx.user.id); + return super.update(bean); + } + @Post('/info') + async info(@Query('id') id) { + await this.service.checkUserId(id, this.ctx.user.id); + return super.info(id); + } + + @Post('/delete') + async delete(@Query('id') id) { + await this.service.checkUserId(id, this.ctx.user.id); + return super.delete(id); + } + + @Post('/define') + async define(@Query('type') type) { + const provider = this.service.getDefineByType(type); + return this.ok(provider); + } + + @Post('/accessTypeDict') + async getAccessTypeDict() { + const list = this.service.getDefineList(); + const dict = []; + for (const item of list) { + dict.push({ + value: item.name, + label: item.title, + }); + } + return this.ok(dict); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/controller/dns-provider-controller.ts b/packages/ui/certd-server/src/modules/pipeline/controller/dns-provider-controller.ts new file mode 100644 index 000000000..99f11467b --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/controller/dns-provider-controller.ts @@ -0,0 +1,40 @@ +import { + ALL, + Controller, + Inject, + Post, + Provide, + Query, +} from '@midwayjs/decorator'; +import { DnsProviderService } from '../service/dns-provider-service'; +import { BaseController } from '../../../basic/base-controller'; + +/** + * 插件 + */ +@Provide() +@Controller('/api/pi/dnsProvider') +export class DnsProviderController extends BaseController { + @Inject() + service: DnsProviderService; + + @Post('/list') + async list(@Query(ALL) query) { + query.userId = this.ctx.user.id; + const list = this.service.getList(); + return this.ok(list); + } + + @Post('/dnsProviderTypeDict') + async getDnsProviderTypeDict() { + const list = this.service.getList(); + const dict = []; + for (const item of list) { + dict.push({ + value: item.name, + label: item.title, + }); + } + return this.ok(dict); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/controller/history-controller.ts b/packages/ui/certd-server/src/modules/pipeline/controller/history-controller.ts new file mode 100644 index 000000000..a8410979d --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/controller/history-controller.ts @@ -0,0 +1,106 @@ +import { + ALL, + Body, + Controller, + Inject, + Post, + Provide, + Query, +} from '@midwayjs/decorator'; +import { CrudController } from '../../../basic/crud-controller'; +import { PipelineEntity } from '../entity/pipeline'; +import { HistoryService } from '../service/history-service'; +import { HistoryLogService } from '../service/history-log-service'; +import { HistoryEntity } from '../entity/history'; +import { HistoryLogEntity } from '../entity/history-log'; + +/** + * 证书 + */ +@Provide() +@Controller('/api/pi/history') +export class HistoryController extends CrudController { + @Inject() + service: HistoryService; + @Inject() + logService: HistoryLogService; + + getService() { + return this.service; + } + + @Post('/page') + async page(@Body(ALL) body) { + body.query.userId = this.ctx.user.id; + return super.page(body); + } + + @Post('/list') + async list(@Body(ALL) body) { + body.userId = this.ctx.user.id; + if (body.pipelineId == null) { + return this.ok([]); + } + const buildQuery = qb => { + qb.limit(10); + }; + const listRet = await this.getService().list( + body, + { prop: 'id', asc: false }, + buildQuery + ); + return this.ok(listRet); + } + + @Post('/add') + async add(@Body(ALL) bean: PipelineEntity) { + bean.userId = this.ctx.user.id; + return super.add(bean); + } + + @Post('/update') + async update(@Body(ALL) bean) { + await this.service.checkUserId(bean.id, this.ctx.user.id); + return super.update(bean); + } + + @Post('/save') + async save(@Body(ALL) bean: HistoryEntity) { + bean.userId = this.ctx.user.id; + if (bean.id > 0) { + await this.service.checkUserId(bean.id, this.ctx.user.id); + } + await this.service.save(bean); + return this.ok(bean.id); + } + + @Post('/saveLog') + async saveLog(@Body(ALL) bean: HistoryLogEntity) { + bean.userId = this.ctx.user.id; + if (bean.id > 0) { + await this.service.checkUserId(bean.id, this.ctx.user.id); + } + await this.logService.save(bean); + return this.ok(bean.id); + } + + @Post('/delete') + async delete(@Query('id') id) { + await this.service.checkUserId(id, this.ctx.user.id); + return super.delete(id); + } + + @Post('/detail') + async detail(@Query('id') id) { + await this.service.checkUserId(id, this.ctx.user.id); + const detail = await this.service.detail(id); + return this.ok(detail); + } + + @Post('/logs') + async logs(@Query('id') id) { + await this.logService.checkUserId(id, this.ctx.user.id); + const logInfo = await this.logService.info(id); + return this.ok(logInfo); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/controller/pipeline-controller.ts b/packages/ui/certd-server/src/modules/pipeline/controller/pipeline-controller.ts new file mode 100644 index 000000000..c19685439 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/controller/pipeline-controller.ts @@ -0,0 +1,77 @@ +import { + ALL, + Body, + Controller, + Inject, + Post, + Provide, + Query, +} from '@midwayjs/decorator'; +import { CrudController } from '../../../basic/crud-controller'; +import { PipelineService } from '../service/pipeline-service'; +import { PipelineEntity } from '../entity/pipeline'; + +/** + * 证书 + */ +@Provide() +@Controller('/api/pi/pipeline') +export class PipelineController extends CrudController { + @Inject() + service: PipelineService; + + getService() { + return this.service; + } + + @Post('/page') + async page(@Body(ALL) body) { + body.query.userId = this.ctx.user.id; + const buildQuery = qb => { + qb.where({}); + }; + return super.page({ ...body, buildQuery }); + } + + @Post('/add') + async add(@Body(ALL) bean: PipelineEntity) { + bean.userId = this.ctx.user.id; + return super.add(bean); + } + + @Post('/update') + async update(@Body(ALL) bean) { + await this.service.checkUserId(bean.id, this.ctx.user.id); + return super.update(bean); + } + + @Post('/save') + async save(@Body(ALL) bean: PipelineEntity) { + bean.userId = this.ctx.user.id; + if (bean.id > 0) { + await this.service.checkUserId(bean.id, this.ctx.user.id); + } + await this.service.save(bean); + return this.ok(bean.id); + } + + @Post('/delete') + async delete(@Query('id') id) { + await this.service.checkUserId(id, this.ctx.user.id); + return super.delete(id); + } + + @Post('/detail') + async detail(@Query('id') id) { + await this.service.checkUserId(id, this.ctx.user.id); + const detail = await this.service.detail(id); + return this.ok(detail); + } + + @Post('/trigger') + async trigger(@Query('id') id) { + await this.service.checkUserId(id, this.ctx.user.id); + await this.service.trigger(id); + return this.ok({}); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/controller/plugin-controller.ts b/packages/ui/certd-server/src/modules/pipeline/controller/plugin-controller.ts new file mode 100644 index 000000000..4501d2c85 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/controller/plugin-controller.ts @@ -0,0 +1,27 @@ +import { + ALL, + Controller, + Inject, + Post, + Provide, + Query, +} from '@midwayjs/decorator'; +import { BaseController } from '../../../basic/base-controller'; +import { PluginService } from '../service/plugin-service'; + +/** + * 插件 + */ +@Provide() +@Controller('/api/pi/plugin') +export class PluginController extends BaseController { + @Inject() + service: PluginService; + + @Post('/list') + async list(@Query(ALL) query) { + query.userId = this.ctx.user.id; + const list = this.service.getList(); + return this.ok(list); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/entity/access.ts b/packages/ui/certd-server/src/modules/pipeline/entity/access.ts new file mode 100644 index 000000000..8855c1ebf --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/entity/access.ts @@ -0,0 +1,33 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +/** + * 授权配置 + */ +@Entity('cd_access') +export class AccessEntity { + @PrimaryGeneratedColumn() + id: number; + @Column({ name: 'user_id', comment: '用户id' }) + userId: number; + @Column({ comment: '名称', length: 100 }) + name: string; + + @Column({ comment: '类型', length: 100 }) + type: string; + + @Column({ name: 'setting', comment: '设置', length: 1024, nullable: true }) + setting: string; + + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; +} diff --git a/packages/ui/certd-server/src/modules/pipeline/entity/history-log.ts b/packages/ui/certd-server/src/modules/pipeline/entity/history-log.ts new file mode 100644 index 000000000..bdeeeeaf9 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/entity/history-log.ts @@ -0,0 +1,41 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity('pi_history_log') +export class HistoryLogEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ name: 'user_id', comment: '用户id' }) + userId: number; + + @Column({ name: 'pipeline_id', comment: '流水线' }) + pipelineId: number; + + @Column({ name: 'history_id', comment: '历史id' }) + historyId: number; + + @Column({ + name: 'node_id', + comment: '任务节点id', + length: 100, + nullable: true, + }) + nodeId: string; + + @Column({ comment: '日志内容', length: 40960, nullable: true }) + logs: string; + + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; +} diff --git a/packages/ui/certd-server/src/modules/pipeline/entity/history.ts b/packages/ui/certd-server/src/modules/pipeline/entity/history.ts new file mode 100644 index 000000000..ab1425f54 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/entity/history.ts @@ -0,0 +1,38 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity('pi_history') +export class HistoryEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ name: 'user_id', comment: '用户id' }) + userId: number; + + @Column({ name: 'pipeline_id', comment: '流水线' }) + pipelineId: number; + @Column({ comment: '运行状态', length: 40960, nullable: true }) + pipeline: string; + + @Column({ comment: '结果状态', length: 20, nullable: true }) + status: string; + + @Column({ + name: 'end_time', + comment: '结束时间', + nullable: true, + }) + endTime: Date; + + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; +} diff --git a/packages/ui/certd-server/src/modules/pipeline/entity/pipeline.ts b/packages/ui/certd-server/src/modules/pipeline/entity/pipeline.ts new file mode 100644 index 000000000..c9f74d5b7 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/entity/pipeline.ts @@ -0,0 +1,52 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity('pi_pipeline') +export class PipelineEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ name: 'user_id', comment: '用户id' }) + userId: number; + + @Column({ name: 'title', comment: '标题' }) + title: number; + + @Column({ comment: '配置', length: 40960 }) + content: string; + + @Column({ + name: 'keep_history_count', + comment: '历史记录保持数量', + nullable: true, + }) + keepHistoryCount: number; + + @Column({ comment: '备注', length: 100, nullable: true }) + remark: string; + + @Column({ comment: '状态', length: 100, nullable: true }) + status: string; + + @Column({ comment: '启用/禁用', nullable: true, default: false }) + disabled: boolean; + + @Column({ + name: 'last_history_time', + comment: '最后一次执行时间', + nullable: true, + }) + lastHistoryTime: number; + + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; +} diff --git a/packages/ui/certd-server/src/modules/pipeline/entity/storage.ts b/packages/ui/certd-server/src/modules/pipeline/entity/storage.ts new file mode 100644 index 000000000..96138c478 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/entity/storage.ts @@ -0,0 +1,35 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity('pi_storage') +export class StorageEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ name: 'user_id', comment: '用户id' }) + userId: number; + + @Column({ name: 'scope', comment: '范围' }) + scope: string; + + @Column({ name: 'namespace', comment: '命名空间' }) + namespace: string; + + @Column({ comment: 'key', length: 100, nullable: true }) + key: string; + + @Column({ comment: 'value', length: 40960, nullable: true }) + value: string; + + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; +} diff --git a/packages/ui/certd-server/src/modules/pipeline/entity/vo/history-detail.ts b/packages/ui/certd-server/src/modules/pipeline/entity/vo/history-detail.ts new file mode 100644 index 000000000..39d6efdbe --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/entity/vo/history-detail.ts @@ -0,0 +1,12 @@ +import { HistoryEntity } from '../history'; +import { HistoryLogEntity } from '../history-log'; + +export class HistoryDetail { + history: HistoryEntity; + log: HistoryLogEntity; + + constructor(history: HistoryEntity, log: HistoryLogEntity) { + this.history = history; + this.log = log; + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/entity/vo/pipeline-detail.ts b/packages/ui/certd-server/src/modules/pipeline/entity/vo/pipeline-detail.ts new file mode 100644 index 000000000..e7a5f4b32 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/entity/vo/pipeline-detail.ts @@ -0,0 +1,13 @@ +import { PipelineEntity } from '../pipeline'; +import { HistoryEntity } from '../history'; +import { HistoryLogEntity } from '../history-log'; + +export class PipelineDetail { + pipeline: PipelineEntity; + constructor(pipeline: PipelineEntity) { + this.pipeline = pipeline; + } + + last: HistoryEntity; + logs: HistoryLogEntity[]; +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/access-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/access-service.ts new file mode 100644 index 000000000..b911fc295 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/access-service.ts @@ -0,0 +1,44 @@ +import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator"; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { AccessEntity } from '../entity/access'; +import { + accessRegistry, + IAccessService, +} from '@certd/pipeline'; + +/** + * 授权 + */ +@Provide() +@Scope(ScopeEnum.Singleton) +export class AccessService + extends BaseService + implements IAccessService +{ + @InjectEntityModel(AccessEntity) + repository: Repository; + + getRepository() { + return this.repository; + } + + async getById(id: any): Promise { + const entity = await this.info(id); + // const access = accessRegistry.get(entity.type); + const setting = JSON.parse(entity.setting); + return { + id: entity.id, + ...setting, + }; + } + + getDefineList() { + return accessRegistry.getDefineList(); + } + + getDefineByType(type) { + return accessRegistry.getDefine(type); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/db-storage.ts b/packages/ui/certd-server/src/modules/pipeline/service/db-storage.ts new file mode 100644 index 000000000..c95357315 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/db-storage.ts @@ -0,0 +1,47 @@ +import { IStorage } from '@certd/pipeline/src/core/storage'; +import { StorageService } from './storage-service'; + +export class DbStorage implements IStorage { + /** + * 范围: user / pipeline / runtime / task + */ + storageService: StorageService; + userId: number; + constructor(userId: number, storageService: StorageService) { + this.userId = userId; + this.storageService = storageService; + } + + async get( + scope: string, + namespace: string, + key: string + ): Promise { + const storageEntity = await this.storageService.get({ + userId: this.userId, + scope: scope, + namespace: namespace, + key, + }); + + if (storageEntity != null) { + return storageEntity.value; + } + return null; + } + + async set( + scope: string, + namespace: string, + key: string, + value: string + ): Promise { + await this.storageService.set({ + userId: this.userId, + scope: scope, + namespace: namespace, + key, + value, + }); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/dns-provider-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/dns-provider-service.ts new file mode 100644 index 000000000..0473841b8 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/dns-provider-service.ts @@ -0,0 +1,9 @@ +import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator"; +import { dnsProviderRegistry } from '@certd/plugin-cert'; +@Provide() +@Scope(ScopeEnum.Singleton) +export class DnsProviderService { + getList() { + return dnsProviderRegistry.getDefineList(); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/history-log-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/history-log-service.ts new file mode 100644 index 000000000..46aaa74b8 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/history-log-service.ts @@ -0,0 +1,27 @@ +import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator"; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { HistoryLogEntity } from '../entity/history-log'; + +/** + * 证书申请 + */ +@Provide() +@Scope(ScopeEnum.Singleton) +export class HistoryLogService extends BaseService { + @InjectEntityModel(HistoryLogEntity) + repository: Repository; + + getRepository() { + return this.repository; + } + + async save(bean: HistoryLogEntity) { + if (bean.id > 0) { + await this.update(bean); + } else { + await this.add(bean); + } + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/history-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/history-service.ts new file mode 100644 index 000000000..832b9ea86 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/history-service.ts @@ -0,0 +1,81 @@ +import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/decorator"; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { HistoryEntity } from '../entity/history'; +import { PipelineEntity } from '../entity/pipeline'; +import { HistoryDetail } from '../entity/vo/history-detail'; +import { HistoryLogService } from './history-log-service'; + +/** + * 证书申请 + */ +@Provide() +@Scope(ScopeEnum.Singleton) +export class HistoryService extends BaseService { + @InjectEntityModel(HistoryEntity) + repository: Repository; + @Inject() + logService: HistoryLogService; + getRepository() { + return this.repository; + } + + async save(bean: HistoryEntity) { + if (bean.id > 0) { + await this.update(bean); + } else { + await this.add(bean); + } + } + + async detail(historyId: string) { + const entity = await this.info(historyId); + const log = await this.logService.info(historyId); + return new HistoryDetail(entity, log); + } + + async start(pipeline: PipelineEntity) { + const bean = { + userId: pipeline.userId, + pipelineId: pipeline.id, + title: pipeline.title, + status: 'start', + }; + const { id } = await this.add(bean); + //清除大于pipeline.keepHistoryCount的历史记录 + this.clear(pipeline.id, pipeline.keepHistoryCount); + return id; + } + + private async clear(pipelineId: number, keepCount = 30) { + const count = await this.repository.count({ + where: { + pipelineId, + }, + }); + if (count <= keepCount) { + return; + } + let shouldDeleteCount = count - keepCount; + const deleteCountBatch = 100; + while (shouldDeleteCount > 0) { + const list = await this.repository.find({ + select: { + id: true, + }, + where: { + pipelineId, + }, + order: { + id: 'ASC', + }, + skip: 0, + take: deleteCountBatch, + }); + await this.repository.remove(list); + + shouldDeleteCount -= deleteCountBatch; + } + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts new file mode 100644 index 000000000..b267f93e3 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts @@ -0,0 +1,241 @@ +import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/decorator'; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { In, Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { PipelineEntity } from '../entity/pipeline'; +import { PipelineDetail } from '../entity/vo/pipeline-detail'; +import { Executor, Pipeline, RunHistory } from '@certd/pipeline'; +import { AccessService } from './access-service'; +import { DbStorage } from './db-storage'; +import { StorageService } from './storage-service'; +import { Cron } from '../../../plugins/cron/cron'; +import { HistoryService } from './history-service'; +import { HistoryEntity } from '../entity/history'; +import { HistoryLogEntity } from '../entity/history-log'; +import { HistoryLogService } from './history-log-service'; +import { logger } from '../../../utils/logger'; + +/** + * 证书申请 + */ +@Provide() +@Scope(ScopeEnum.Singleton) +export class PipelineService extends BaseService { + @InjectEntityModel(PipelineEntity) + repository: Repository; + + @Inject() + accessService: AccessService; + @Inject() + storageService: StorageService; + @Inject() + historyService: HistoryService; + @Inject() + historyLogService: HistoryLogService; + + @Inject() + cron: Cron; + + getRepository() { + return this.repository; + } + + async update(entity) { + await super.update(entity); + + await this.registerTriggerById(entity.id); + } + + private async registerTriggerById(pipelineId) { + if (pipelineId == null) { + return; + } + const info = await this.info(pipelineId); + if (info && !info.disabled) { + const pipeline = JSON.parse(info.content); + this.registerTriggers(pipeline); + } + } + + /** + * 获取详情 + * @param id + */ + async detail(id) { + const pipeline = await this.info(id); + return new PipelineDetail(pipeline); + } + + async save(bean: PipelineEntity) { + const pipeline = JSON.parse(bean.content); + bean.title = pipeline.title; + await this.addOrUpdate(bean); + await this.registerTriggerById(bean.id); + } + + /** + * 应用启动后初始加载记录 + */ + async onStartup() { + const idEntityList = await this.repository.find({ + select: { + id: true, + }, + where: { + disabled: false, + }, + }); + const ids = idEntityList.map(item => { + return item.id; + }); + + //id 分段 + const idsSpan = []; + let arr = []; + for (let i = 0; i < ids.length; i++) { + if (i % 20 === 0) { + arr = []; + idsSpan.push(arr); + } + arr.push(ids[i]); + } + + //分段加载记录 + for (const idArr of idsSpan) { + const list = await this.repository.findBy({ + id: In(idArr), + }); + + for (const entity of list) { + const pipeline = JSON.parse(entity.content ?? '{}'); + this.registerTriggers(pipeline); + } + } + logger.info('定时器数量:', this.cron.getList()); + } + + registerTriggers(pipeline?: Pipeline) { + if (pipeline?.triggers == null) { + return; + } + for (const trigger of pipeline.triggers) { + this.registerCron(pipeline.id, trigger); + } + } + + async trigger(id) { + this.cron.register({ + name: `pipeline.${id}.trigger.once`, + cron: null, + job: async () => { + await this.run(id, null); + }, + }); + logger.info('定时器数量:', this.cron.getList()); + } + + registerCron(pipelineId, trigger) { + let cron = trigger.props?.cron; + if (cron == null) { + return; + } + if(cron.startsWith("*")){ + cron = "0"+ cron.substring(1,cron.length) + return + } + this.cron.register({ + name: this.buildCronKey(pipelineId, trigger.id), + cron: cron, + job: async () => { + logger.info('定时任务触发:', pipelineId, trigger.id); + await this.run(pipelineId, trigger.id); + }, + }); + } + + async run(id, triggerId) { + const entity: PipelineEntity = await this.info(id); + const pipeline = JSON.parse(entity.content); + + if (!pipeline.stages || pipeline.stages.length === 0) { + return; + } + + const triggerType = this.getTriggerType(triggerId, pipeline); + if (triggerType == null) { + return; + } + + const onChanged = async (history: RunHistory) => { + //保存执行历史 + await this.saveHistory(history); + }; + + const userId = entity.userId; + const historyId = await this.historyService.start(entity); + + const executor = new Executor({ + userId, + pipeline, + onChanged, + accessService: this.accessService, + storage: new DbStorage(userId, this.storageService), + }); + + await executor.run(historyId, triggerType); + } + + private getTriggerType(triggerId, pipeline) { + let triggerType = 'user'; + if (triggerId != null) { + //如果不是手动触发 + //查找trigger + const found = this.findTrigger(pipeline, triggerId); + if (!found) { + //如果没有找到triggerId,说明被用户删掉了,这里再删除一次 + this.cron.remove(this.buildCronKey(pipeline.id, triggerId)); + triggerType = null; + } else { + logger.info('timer trigger:' + found.id, found.title, found.cron); + triggerType = 'timer'; + } + } + return triggerType; + } + + private buildCronKey(pipelineId, triggerId) { + return `pipeline.${pipelineId}.trigger.${triggerId}`; + } + + private findTrigger(pipeline, triggerId) { + for (const trigger of pipeline.triggers) { + if (trigger.id === triggerId) { + return trigger; + } + } + return; + } + + private async saveHistory(history: RunHistory) { + //修改pipeline状态 + const pipelineEntity = new PipelineEntity(); + pipelineEntity.id = parseInt(history.pipeline.id); + pipelineEntity.status = history.pipeline.status.status; + pipelineEntity.lastHistoryTime = history.pipeline.status.startTime; + await this.update(pipelineEntity); + + const entity: HistoryEntity = new HistoryEntity(); + entity.id = parseInt(history.id); + entity.userId = history.pipeline.userId; + entity.pipeline = JSON.stringify(history.pipeline); + await this.historyService.save(entity); + + const logEntity: HistoryLogEntity = new HistoryLogEntity(); + logEntity.id = entity.id; + logEntity.userId = entity.userId; + logEntity.pipelineId = entity.pipelineId; + logEntity.historyId = entity.id; + logEntity.logs = JSON.stringify(history.logs); + await this.historyLogService.addOrUpdate(logEntity); + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/plugin-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/plugin-service.ts new file mode 100644 index 000000000..c02715450 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/plugin-service.ts @@ -0,0 +1,15 @@ +import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator"; +import { pluginRegistry } from '@certd/pipeline'; +@Provide() +@Scope(ScopeEnum.Singleton) +export class PluginService { + getList() { + const collection = pluginRegistry.storage; + const list = []; + for (const key in collection) { + const Plugin = collection[key]; + list.push({ ...Plugin.define, key }); + } + return list; + } +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/storage-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/storage-service.ts new file mode 100644 index 000000000..bcf862964 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/storage-service.ts @@ -0,0 +1,56 @@ +import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator"; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { BaseService } from '../../../basic/base-service'; +import { StorageEntity } from '../entity/storage'; + +/** + */ +@Provide() +@Scope(ScopeEnum.Singleton) +export class StorageService extends BaseService { + @InjectEntityModel(StorageEntity) + repository: Repository; + + getRepository() { + return this.repository; + } + + async get(where: { + scope: any; + namespace: any; + userId: number; + key: string; + }) { + if (where.userId == null) { + throw new Error('userId 不能为空'); + } + return await this.repository.findOne({ + where, + }); + } + + async set(entity: { + id?: any; + scope: any; + namespace: any; + userId: number; + value: string; + key: string; + }) { + entity.id = null; + const query = { ...entity }; + delete query.value; + const ret = await this.get(query); + if (ret != null) { + entity.id = ret.id; + if (ret.userId !== entity.userId) { + throw new Error('您没有权限修改此数据'); + } + await this.repository.save(entity); + } else { + await this.repository.insert(entity); + } + return; + } +} diff --git a/packages/ui/certd-server/src/plugins/cron/configuration.ts b/packages/ui/certd-server/src/plugins/cron/configuration.ts new file mode 100644 index 000000000..b9a6b89ea --- /dev/null +++ b/packages/ui/certd-server/src/plugins/cron/configuration.ts @@ -0,0 +1,27 @@ +import { Config, Configuration, Logger } from '@midwayjs/decorator'; +import { ILogger } from '@midwayjs/logger'; +import { IMidwayContainer } from '@midwayjs/core'; +import { Cron } from './cron'; + +// ... (see below) ... +@Configuration({ + namespace: 'cron', + //importConfigs: [join(__dirname, './config')], +}) +export class CronConfiguration { + @Config() + config; + @Logger() + logger: ILogger; + + cron: Cron; + async onReady(container: IMidwayContainer) { + this.logger.info('cron start'); + this.cron = new Cron({ + logger: this.logger, + ...this.config, + }); + container.registerObject('cron', this.cron); + this.logger.info('cron started'); + } +} diff --git a/packages/ui/certd-server/src/plugins/cron/cron.ts b/packages/ui/certd-server/src/plugins/cron/cron.ts new file mode 100644 index 000000000..e0380707e --- /dev/null +++ b/packages/ui/certd-server/src/plugins/cron/cron.ts @@ -0,0 +1,38 @@ +import cron from 'node-cron'; +export type CronTask = { + /** + * 为空则为单次执行 + */ + cron: string; + job: () => Promise; + name: string; +}; +export class Cron { + logger; + constructor(opts) { + this.logger = opts.logger; + } + + register(task: CronTask) { + if (!task.cron) { + this.logger.info(`[cron] register once : [${task.name}]`); + task.job(); + return; + } + this.logger.info(`[cron] register cron : [${task.name}] ,${task.cron}`); + cron.schedule(task.cron, task.job, { + name: task.name, + }); + } + + remove(taskName: string) { + this.logger.info(`[cron] remove : [${taskName}]`); + const tasks = cron.getTasks() as Map; + tasks.delete(taskName); + } + + getList() { + const tasks = cron.getTasks(); + return tasks.size; + } +} diff --git a/packages/ui/certd-server/src/plugins/cron/index.ts b/packages/ui/certd-server/src/plugins/cron/index.ts new file mode 100644 index 000000000..69c716a09 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/cron/index.ts @@ -0,0 +1,5 @@ +// src/index.ts +export { CronConfiguration as Configuration } from './configuration'; +// export * from './controller/user'; +// export * from './controller/api'; +// export * from './service/user'; diff --git a/packages/ui/certd-server/src/plugins/pipeline/index.ts b/packages/ui/certd-server/src/plugins/pipeline/index.ts new file mode 100644 index 000000000..388562a88 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/pipeline/index.ts @@ -0,0 +1,6 @@ +// src/index.ts +import '@certd/plugin-all'; +export { PipelineConfiguration as Configuration } from '@certd/pipeline'; +// export * from './controller/user'; +// export * from './controller/api'; +// export * from './service/user'; diff --git a/packages/ui/certd-server/src/plugins/pipeline/test/echo-plugin.ts b/packages/ui/certd-server/src/plugins/pipeline/test/echo-plugin.ts new file mode 100644 index 000000000..7adee962d --- /dev/null +++ b/packages/ui/certd-server/src/plugins/pipeline/test/echo-plugin.ts @@ -0,0 +1,27 @@ +import { ILogger } from "@midwayjs/logger"; +import { ITaskPlugin,Autowire, IsTaskPlugin, TaskInput } from "@certd/pipeline"; + +@IsTaskPlugin({ + name: "EchoPlugin", + title: "测试插件", + desc: "test", +}) +export class EchoPlugin implements ITaskPlugin { + @TaskInput({ + title: "测试属性", + component: { + name: "text", + }, + }) + test?: string; + + @Autowire() + // @ts-ignore + logger: ILogger; + + async onInit(){} + + async execute(): Promise { + return Promise.resolve(undefined); + } +} diff --git a/packages/ui/certd-server/src/utils/logger.ts b/packages/ui/certd-server/src/utils/logger.ts new file mode 100644 index 000000000..d4fea9ea7 --- /dev/null +++ b/packages/ui/certd-server/src/utils/logger.ts @@ -0,0 +1,12 @@ +const log4js = require('log4js'); +const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info'; +const path = require('path'); +const filename = path.join('/logs/server.log'); +log4js.configure({ + appenders: { + std: { type: 'stdout', level: 'debug' }, + file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename }, + }, + categories: { default: { appenders: ['std'], level } }, +}); +export const logger = log4js.getLogger('fast'); diff --git a/packages/ui/certd-server/src/utils/random.ts b/packages/ui/certd-server/src/utils/random.ts new file mode 100644 index 000000000..6be6f6e2d --- /dev/null +++ b/packages/ui/certd-server/src/utils/random.ts @@ -0,0 +1,43 @@ +const numbers = '0123456789'; +const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; +const specials = '~!@#$%^*()_+-=[]{}|;:,./<>?'; + +/** + * Generate random string + * @param {Number} length + * @param {Object} options + */ +function randomStr(length, options) { + length || (length = 8); + options || (options = {}); + + let chars = ''; + let result = ''; + + if (options === true) { + chars = numbers + letters; + } else if (typeof options === 'string') { + chars = options; + } else { + if (options.numbers !== false) { + chars += typeof options.numbers === 'string' ? options.numbers : numbers; + } + + if (options.letters !== false) { + chars += typeof options.letters === 'string' ? options.letters : letters; + } + + if (options.specials) { + chars += + typeof options.specials === 'string' ? options.specials : specials; + } + } + + while (length > 0) { + length--; + result += chars[Math.floor(Math.random() * chars.length)]; + } + return result; +} + +export const RandomUtil = { randomStr }; diff --git a/packages/ui/certd-server/test/controller/api.test.ts b/packages/ui/certd-server/test/controller/api.test.ts new file mode 100644 index 000000000..28dcfd035 --- /dev/null +++ b/packages/ui/certd-server/test/controller/api.test.ts @@ -0,0 +1,25 @@ +import { createApp, close, createHttpRequest } from '@midwayjs/mock'; +import { Framework } from '@midwayjs/koa'; +import * as assert from 'assert'; + +describe('test/controller/home.test.ts', () => { + + it('should POST /api/get_user', async () => { + // create app + const app = await createApp(); + + // make request + const result = await createHttpRequest(app).post('/api/get_user').query({ uid: 123 }); + + // use expect by jest + expect(result.status).toBe(200); + expect(result.body.message).toBe('OK'); + + // or use assert + assert.deepStrictEqual(result.status, 200); + assert.deepStrictEqual(result.body.data.uid, '123'); + + // close app + await close(app); + }); +}); diff --git a/packages/ui/certd-server/test/controller/home.test.ts b/packages/ui/certd-server/test/controller/home.test.ts new file mode 100644 index 000000000..2e9578892 --- /dev/null +++ b/packages/ui/certd-server/test/controller/home.test.ts @@ -0,0 +1,26 @@ +import { createApp, close, createHttpRequest } from '@midwayjs/mock'; +import { Framework } from '@midwayjs/koa'; +import * as assert from 'assert'; + +describe('test/controller/home.test.ts', () => { + + it('should GET /', async () => { + // create app + const app = await createApp(); + + // make request + const result = await createHttpRequest(app).get('/'); + + // use expect by jest + expect(result.status).toBe(200); + expect(result.text).toBe('Hello Midwayjs!'); + + // or use assert + assert.deepStrictEqual(result.status, 200); + assert.deepStrictEqual(result.text, 'Hello Midwayjs!'); + + // close app + await close(app); + }); + +}); diff --git a/packages/ui/certd-server/tsconfig.json b/packages/ui/certd-server/tsconfig.json new file mode 100644 index 000000000..d4f44496e --- /dev/null +++ b/packages/ui/certd-server/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "moduleResolution": "node", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "inlineSourceMap":true, + "noImplicitThis": true, + "noUnusedLocals": true, + "stripInternal": true, + "skipLibCheck": true, + "esModuleInterop": true, + "pretty": true, + "declaration": true, + "typeRoots": [ "./typings", "./node_modules/@types"], + "outDir": "dist" + }, + "exclude": [ + "dist", + "node_modules", + "test" + ] +}