1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-07-27 04:20:43 +08:00

11 Commits

Author SHA1 Message Date
小莫唐尼 0da5feb2ee 修复:修复封面图片,如果是随机图api不生效的BUG 2022-12-10 14:30:32 +08:00
小莫唐尼 e46c5c2b6f 新增:文章版权说明;
新增:文章详情点击标签跳转查询标签下的文章列表;
优化:调整markdown文字大小
2022-12-10 14:24:36 +08:00
小莫唐尼 70c5b34de0 新增:留言评论头像形状设置;
修复:修复文章管理设置文章标签在小程序端不显示的bug;
修复:修复后台管理工作台因为无法读取用户信息页面不显示bug;
优化:优化友情链接动画效果
2022-12-10 12:26:47 +08:00
小莫唐尼 9d3ebac076 新增:更新halo.config.js 配置参数;
更新:更新项目UI框架;
修复:修复友链列表丢失白色背景色BUG;
2022-12-09 18:10:47 +08:00
小莫唐尼 3fd3b2247a 修复:后台管理文章设置BUG;
修复:友链列表不显示数据BUG;
修复:应用设置点击返回,触发提示BUG;
2022-12-09 10:48:00 +08:00
小莫唐尼 3702cb1618 新增:联系博主页面顶部返回按钮;
修复:修复 默认封面图、默认图片、默认头像在使用随机api时候无法显示的BUG;
修复:后台管理新增文章发布失败BUG;
删除:去除联系博主页面中的 联系博主 按钮;
优化:对友链页面进行重写;
优化:对部分页面和功能进行优化。
2022-12-09 00:24:39 +08:00
小莫唐尼 473655171b 修改:修正项目中的开源协议信息 2022-12-07 00:39:18 +08:00
小莫唐尼 9e2f090747 修改:更正开源协议 2022-12-07 00:33:26 +08:00
小莫唐尼 e05d0d7ce5 优化:重写友链列表,兼容微信小程序;
优化:解决分类功能滚动回弹问题;
2022-12-06 23:18:44 +08:00
小莫唐尼 3ba10fa105 修复:修复小程序文章图片不显示BUG 2022-12-06 18:36:06 +08:00
小莫唐尼 acc9070134 新增:首次启动页面配置 2022-12-06 18:02:36 +08:00
118 changed files with 2433 additions and 7699 deletions
+4
View File
@@ -10,6 +10,10 @@
{
"launchtype" : "local"
},
"h5" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
+1 -5
View File
@@ -64,14 +64,10 @@ export default {
@import './common/icons/mphtmliconfont.css';
/* #ifndef MP-WEIXIN */
@import './common/markdown/markdown.scss'; //引入markdown呈现
@import './common/markdown/markdown.scss';
/* #endif */
page {
// background-color: #f3f5f7;
// background-color: #f7f7f7;
// background-color: #ffffff;
background-color: #fafafa;
// background-color: #f4f5f5;
}
</style>
+657 -16
View File
@@ -1,20 +1,661 @@
The MIT License (MIT)
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (c) 2018 RuoYi
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
+2 -2
View File
@@ -98,8 +98,8 @@
## 🔍 许可证
<a href="https://gitee.com/ialley-workshop-open/uni-halo/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
uni-halo 使用 MIT 协议开源,请遵守开源协议。
uni-halo 使用 AGPL-3.0 协议开源,请遵守开源协议。
## 🏭 贡献
+1
View File
@@ -24,6 +24,7 @@ const createOrEditModel = {
"value": "string"
}],
"originalContent": "string",
"formatContent": "",
"password": "string",
"slug": "string",
"status": "DRAFT",
+2
View File
@@ -16,6 +16,7 @@ import archive from './archive.js'
import article from './article.js'
import blogger from './blogger.js'
import category from './category.js'
import tag from './tag.js'
import comment from './comment.js'
import journal from './journal.js'
import link from './link.js'
@@ -44,6 +45,7 @@ const ApiManager = {
...article,
...blogger,
...category,
...tag,
...comment,
...journal,
...link,
+24
View File
@@ -0,0 +1,24 @@
/**
* 标签接口
* @see https://api.halo.run/content-api.html#tag/tag-controller
*/
import HttpHandler from '@/common/http/request.js'
export default {
/**
* 查询标签列表
* @param {Object} params 查询参数
*/
getTagList: (params) => {
return HttpHandler.Get('/api/content/tags', params)
},
/**
* 查询标签下的文章
* @param {String} slug 别名
* @param {Object} params 查询参数
*/
getTagPostsList: (slug, params) => {
return HttpHandler.Get(`/api/content/tags/${slug}/posts`, params)
},
}
+7 -7
View File
@@ -44,47 +44,47 @@ export default {
color: var(--main);
line-height: 24px;
position: relative;
font-size:1.25em;
font-size:1.2em;
`,
h2: `
color: var(--main);
line-height: 24px;
position: relative;
margin: 22px 0 16px;
font-size: 1.2em;
font-size: 1.16em;
`,
h3: `
color: var(--main);
line-height: 24px;
position: relative;
margin: 26px 0 18px;
font-size: 1.3em;
font-size: 1.14em;
`,
h4: `
color: var(--main);
line-height: 24px;
margin-bottom: 18px;
position: relative;
font-size: 1.18em;
font-size: 1.2em;
`,
h5: `
color: var(--main);
line-height: 24px;
margin-bottom: 14px;
position: relative;
font-size: 1em;
font-size: 16px;
`,
h6: `
color: #303133;
line-height: 24px;
margin-bottom: 14px;
position: relative;
font-size: 1em;
font-size: 14px;
`,
p: `
line-height: 1.65;
margin-bottom: 14px;
font-size: 15px;
font-size: 14px;
`,
'code': ` `,
strong: 'font-weight: 700;color: rgb(248, 57, 41);',
+1 -1
View File
@@ -24,7 +24,7 @@ export default {
computed: {
// 获取全局应用设置
globalAppSettings() {
return uni.$tm.vx.getters().setting.getSettings;
return uni.$tm.vx.getters().getSettings;
}
},
created() {
+2 -2
View File
@@ -160,7 +160,7 @@ export default {
padding: 0;
.left {
width: 100%;
height: 300rpx;
height: 340rpx;
.thumbnail {
::v-deep uni-image {
border-radius: 6rpx 6rpx 0 0 !important;
@@ -188,7 +188,7 @@ export default {
flex-direction: column;
.left {
width: 100%;
height: 260rpx;
height: 340rpx;
order: 2;
margin-top: 24rpx;
}
+6 -1
View File
@@ -1,5 +1,5 @@
<template>
<view class="">
<view class="cache-image">
<view v-if="loadStatus == 'loading'" class="img-loading" :style="[imgStyle, loadStyle]">
<!-- <text class="img-load-icon iconfont icon-loading"></text>
<text class="img-load-text">{{ loadText }}</text> -->
@@ -108,6 +108,7 @@ export default {
}
},
created() {
console.log('cache-image', this.url);
this.imgStyle = {
width: this.width,
height: this.height,
@@ -144,6 +145,10 @@ export default {
</script>
<style scoped lang="scss">
.cache-image {
width: 100%;
height: 100%;
}
.img-loading,
.img-error {
display: flex;
+28 -8
View File
@@ -1,8 +1,22 @@
<template>
<view class=" comment-item flex flex-col mt-30 pt-24" :class="{ 'child-comment-item': isChild, 'no-solid': !useSolid, classItem }">
<view class="comment-item_user flex">
<image v-if="comment.isAdmin" class="user-avatar" :src="bloggerInfo.avatar" mode="aspectFill" @error="fnOnImageError(comment)"></image>
<image v-else class="user-avatar" :src="comment.avatar" mode="aspectFill" @error="fnOnImageError(comment)"></image>
<image
v-if="comment.isAdmin"
class="user-avatar"
:class="{ 'is-radius': globalAppSettings.isAvatarRadius }"
:src="bloggerInfo.avatar"
mode="aspectFill"
@error="fnOnImageError(comment)"
></image>
<image
v-else
class="user-avatar"
:class="{ 'is-radius': globalAppSettings.isAvatarRadius }"
:src="comment.avatar"
mode="aspectFill"
@error="fnOnImageError(comment)"
></image>
<view class="user-info pl-14">
<view class="author">
<text class="mr-6 text-grey-darken-1 text-size-m">{{ comment.author }}</text>
@@ -11,7 +25,7 @@
<tm-tags v-else :dense="true" color="bg-gradient-light-blue-lighten " size="xs" model="fill">游客</tm-tags>
</view>
<view class="flex mt-4">
<view v-if="false" class="text-size-s text-grey mr-12">IP属地浙江省杭州市</view>
<view v-if="false" class="text-size-s text-grey mr-12">IP属地暂无信息</view>
<view class="time text-size-xs text-grey">
<text class="">{{ $tm.dayjs(comment.createTime).format('YYYY年MM月DD日') }}</text>
<text class="ml-12">{{ $tm.dayjs(comment.createTime).fromNow(true) }}</text>
@@ -19,13 +33,12 @@
</view>
</view>
<view v-if="useActions" class="">
<tm-button size="s" text theme="blue" @click="$emit('on-comment', { type: 'user', comment: comment })">回复</tm-button>
<tm-button v-if="!disallowComment" size="s" text theme="blue" @click="$emit('on-comment', { type: 'user', comment: comment })">回复</tm-button>
<tm-button size="s" text theme="grey" @click="$emit('on-copy', comment.content)">复制</tm-button>
</view>
</view>
<view class="comment-item_content mt-12" :class="{ 'has-bg': useContentBg, 'not-ml': isChild }" @click="$emit('on-detail', comment)" v-html="comment.content"></view>
<!-- <view v-if="useActions" class="comment-item_info text-size-s text-grey">
<text v-if="false" @click="$emit('on-todo')">点赞</text>
<text @click="$emit('on-comment', { type: 'user', comment: comment })">回复</text>
@@ -46,6 +59,10 @@ export default {
type: Array,
default: () => []
},
disallowComment: {
type: Boolean,
default: false
},
useActions: {
type: Boolean,
default: true
@@ -74,7 +91,7 @@ export default {
computed: {
// 获取博主信息
bloggerInfo() {
let blogger = this.$tm.vx.getters().blogger.getBlogger;
let blogger = this.$tm.vx.getters().getBlogger;
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
return blogger;
}
@@ -111,10 +128,13 @@ export default {
.user-avatar {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
box-sizing: border-box;
border: 4rpx solid #ffffff;
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
border: 4rpx solid #ffffff;
border-radius: 12rpx;
&.is-radius {
border-radius: 50%;
}
}
.user-info {
width: 0;
+22 -6
View File
@@ -12,7 +12,6 @@
<!-- <text class="filter-item">全部</text> -->
</view>
</view>
<!-- <view v-if="disallowComment" class="disallow-comment"><tm-empty icon="icon-shiliangzhinengduixiang-" label="文章已开启禁止评论"></tm-empty></view> -->
<!-- 内容区域 -->
<view class="comment-list_content">
<view v-if="loading != 'success'" class="loading-wrap flex">
@@ -27,9 +26,22 @@
</view>
</view>
<block v-else>
<tm-alerts
v-if="disallowComment && dataList.length !== 0"
color="red"
text
:margin="[0, 0]"
:shadow="0"
label="Ծ‸Ծ博主已设置该文章禁止评论!"
:round="3"
close
></tm-alerts>
<view class="empty pt-50" v-if="dataList.length == 0">
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无评论">
<tm-button theme="bg-gradient-light-blue-accent" size="m" v-if="!disallowComment" @click="fnToComment(null)">抢沙发</tm-button>
<tm-empty v-if="disallowComment" icon="icon-shiliangzhinengduixiang-" label="暂无评论">
<text class="text-red text-size-s">- 文章已开启禁止评论 -</text>
</tm-empty>
<tm-empty v-else icon="icon-shiliangzhinengduixiang-" label="暂无评论">
<tm-button theme="bg-gradient-light-blue-accent" size="m" @click="fnToComment(null)">抢沙发</tm-button>
</tm-empty>
</view>
<block v-else>
@@ -40,6 +52,7 @@
:isChild="false"
:comment="comment"
:postId="postId"
:disallowComment="disallowComment"
@on-copy="fnCopyContent"
@on-comment="fnToComment"
@on-todo="fnToDo"
@@ -54,6 +67,7 @@
:isChild="true"
:comment="childComment"
:postId="postId"
:disallowComment="disallowComment"
@on-copy="fnCopyContent"
@on-comment="fnToComment"
@on-todo="fnToDo"
@@ -74,9 +88,11 @@
<script>
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
import tmAlerts from '@/tm-vuetify/components/tm-alerts/tm-alerts.vue';
export default {
name: 'comment-list',
components: { tmEmpty, tmButton },
components: { tmEmpty, tmButton, tmAlerts },
props: {
// 是否禁用评论
disallowComment: {
@@ -200,7 +216,7 @@ export default {
position: relative;
box-sizing: border-box;
padding-left: 24rpx;
font-size: 34rpx;
font-size: 30rpx;
font-weight: bold;
&:before {
@@ -209,7 +225,7 @@ export default {
left: 0rpx;
top: 8rpx;
width: 8rpx;
height: 30rpx;
height: 26rpx;
background-color: rgb(3, 174, 252);
border-radius: 6rpx;
}
+1 -1
View File
@@ -96,7 +96,7 @@ export default {
computed: {
// 获取博主信息
bloggerInfo() {
let blogger = this.$tm.vx.getters().blogger.getBlogger;
let blogger = this.$tm.vx.getters().getBlogger;
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
return blogger;
}
+33 -9
View File
@@ -14,19 +14,43 @@ export default {
showAbout: true, // 显示关于项目入口
uni_halo_logo: 'https://b.925i.cn/uni_halo/uni_halo_logo.png', // uni-halo的logo
apiUrl: '', // Api基础域名(您的halo博客基础域名或者是Halo后台管理系统api地址)
apiAuthorization: '', // Halo中-系统-博客设置-切换到高级选项-API设置-Access key
apiUrl: '', // Api基础域名 [必填] :你的Halo博客基础域名
apiAuthorization: '', // Api认证key [必填] Halo中-系统-博客设置-切换到高级选项-API设置-Access key
title: '', // 博客标题
indexImageUrl: '', // 开屏首页图片
miniCodeImageUrl: '', // 小程序码地址
title: '', // 博客标题 [建议必填]:在某些页面没有设置具体的页面名称时候,使用该值显示
miniCodeImageUrl: '', // 小程序的太阳码/二维码的图片地址 [建议必填]:主要用于文章详情页面的生成海报的功能
aboutProfileImageUrl: '', // 关于页面中的资料卡背景图 [建议必填]
author: {
name: '', // 昵称
avatar: '', // 头像地址
motto: '', // 格言
// 启动页面的配置(页面地址`/pagesA/start/start`
start: {
use: true, // 是否使用首次启动页:用户第一次使用你的应用会显示否则不显示
title: 'uni-halo', // 启动页面中的文字标题
bg: '', // 留空则使用默认 开屏首页背景,可以是颜色值或者图片图片地址
logo: 'https://b.925i.cn/uni_halo/uni_halo_logo.png', // logo
desc1: '全新UI,准备出发', // 描述信息1
desc2: '新触动 新感受 新体验', // 描述信息2
btnText: '全新触发' // 按钮文字
},
// 博主信息
author: {
use: true, // 是否启用这里配置的信息,不启用则获取PC端博客设置的信息
nickname: '', // 昵称
avatar: '', // 头像
email: '', // 邮箱
description: '', // 介绍
},
// 版权信息(用于文章详情)
copyright: {
use: true, // 是否使用版权描述
author: '', // 版权归属说明
description: '', // 版权描述
violation: '', // 侵权处理说明
},
// 社交信息(将会在`/pagesA/contact/contact`页面中展示)
// 具体的某个参数值留空则不展示
social: {
qq: "", // qq号
wechat: "", // 微信号
+160 -157
View File
@@ -1,159 +1,162 @@
{
"name": "uni-halo",
"appid": "__UNI__5748B6E",
"description": "uni-halo博客:基于halo开源博客系统API开发的多端博客。",
"versionName": "1.0.0",
"versionCode": 100,
"transformPx": false,
/* 5+App */
"app-plus": {
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
},
/* */
"modules": {
"OAuth": {},
"Share": {}
},
/* */
"distribute": {
/* android */
"android": {
"permissions": [
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.CAPTURE_VIDEO_OUTPUT\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios": {
"dSYMs": false,
"capabilities": {
"entitlements": {
"com.apple.developer.associated-domains": [
"applinks:static-699a26a5-a6cd-4b54-b603-5155b312a5f5.bspapp.com"
]
}
}
},
/* SDK */
"sdkConfigs": {
"ad": {
"gdt": {},
"csj": {},
"360": {},
"ks": {},
"sigmob": {},
"hw": {},
"bd": {}
},
"oauth": {
"weixin": {
"appid": "wxd971954762ec4744",
"UniversalLinks": "https://static-699a26a5-a6cd-4b54-b603-5155b312a5f5.bspapp.com/uni-universallinks/__UNI__5748B6E/"
}
},
"share": {
"weixin": {
"appid": "wxd971954762ec4744",
"UniversalLinks": "https://static-699a26a5-a6cd-4b54-b603-5155b312a5f5.bspapp.com/uni-universallinks/__UNI__5748B6E/"
}
}
},
"icons": {
"android": {
"hdpi": "unpackage/res/icons/72x72.png",
"xhdpi": "unpackage/res/icons/96x96.png",
"xxhdpi": "unpackage/res/icons/144x144.png",
"xxxhdpi": "unpackage/res/icons/192x192.png"
},
"ios": {
"appstore": "unpackage/res/icons/1024x1024.png",
"ipad": {
"app": "unpackage/res/icons/76x76.png",
"app@2x": "unpackage/res/icons/152x152.png",
"notification": "unpackage/res/icons/20x20.png",
"notification@2x": "unpackage/res/icons/40x40.png",
"proapp@2x": "unpackage/res/icons/167x167.png",
"settings": "unpackage/res/icons/29x29.png",
"settings@2x": "unpackage/res/icons/58x58.png",
"spotlight": "unpackage/res/icons/40x40.png",
"spotlight@2x": "unpackage/res/icons/80x80.png"
},
"iphone": {
"app@2x": "unpackage/res/icons/120x120.png",
"app@3x": "unpackage/res/icons/180x180.png",
"notification@2x": "unpackage/res/icons/40x40.png",
"notification@3x": "unpackage/res/icons/60x60.png",
"settings@2x": "unpackage/res/icons/58x58.png",
"settings@3x": "unpackage/res/icons/87x87.png",
"spotlight@2x": "unpackage/res/icons/80x80.png",
"spotlight@3x": "unpackage/res/icons/120x120.png"
}
}
}
}
},
/* */
"quickapp": {},
/* */
"mp-weixin": {
"appid": "wx1bb161867082c72f",
"setting": {
"urlCheck": false,
"minified": true
},
"usingComponents": true
},
"mp-alipay": {
"usingComponents": true
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true
},
"uniStatistics": {
"enable": false
},
"vueVersion": "2",
"h5": {
"title": "uni-halo",
"devServer": {
"https": false,
"disableHostCheck": true,
"proxy": {
"/api": {
"target": "https://b.925i.cn",
"changeOrigin": true,
"secure": false,
"pathRewrite": {
"^/api": ""
}
}
}
}
},
"_spaceID": "699a26a5-a6cd-4b54-b603-5155b312a5f5"
"name" : "uni-halo",
"appid" : "__UNI__5748B6E",
"description" : "uni-halo博客:基于halo开源博客系统API开发的多端博客。",
"versionName" : "1.0.0",
"versionCode" : 100,
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {
"OAuth" : {},
"Share" : {}
},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.CAPTURE_VIDEO_OUTPUT\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {
"dSYMs" : false,
"capabilities" : {
"entitlements" : {
"com.apple.developer.associated-domains" : [ "applinks:static-699a26a5-a6cd-4b54-b603-5155b312a5f5.bspapp.com" ]
}
}
},
/* SDK */
"sdkConfigs" : {
"ad" : {
"gdt" : {},
"csj" : {},
"360" : {},
"ks" : {},
"sigmob" : {},
"hw" : {},
"bd" : {}
},
"oauth" : {
"weixin" : {
"appid" : "wxd971954762ec4744",
"UniversalLinks" : "https://static-699a26a5-a6cd-4b54-b603-5155b312a5f5.bspapp.com/uni-universallinks/__UNI__5748B6E/"
}
},
"share" : {
"weixin" : {
"appid" : "wxd971954762ec4744",
"UniversalLinks" : "https://static-699a26a5-a6cd-4b54-b603-5155b312a5f5.bspapp.com/uni-universallinks/__UNI__5748B6E/"
}
}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx1bb161867082c72f",
"setting" : {
"urlCheck" : false,
"minified" : true
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2",
"h5" : {
"title" : "uni-halo",
"devServer" : {
"disableHostCheck" : true,
"proxy" : {
"/api" : {
"target" : "https://b.925i.cn",
"changeOrigin" : true,
"secure" : false,
"pathRewrite" : {
"^/api" : ""
}
}
}
},
"optimization" : {
"preload" : true,
"treeShaking" : {
"enable" : true
}
}
},
"_spaceID" : "699a26a5-a6cd-4b54-b603-5155b312a5f5"
}
+19 -3
View File
@@ -201,8 +201,7 @@
"path": "contact/contact",
"style": {
"navigationBarTitleText": "联系博主",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
"enablePullDownRefresh": false
}
}, {
"path": "about/about",
@@ -216,7 +215,24 @@
"navigationBarTitleText": "设置",
"enablePullDownRefresh": false
}
}, {
"path": "tags/tags",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "tag-detail/tag-detail",
"style": {
"navigationBarTitleText": "标签详情",
"enablePullDownRefresh": true,
"app-plus": {
"pullToRefresh": {
"color": "#03a9f4",
"style": "circle"
}
}
}
}]
},
{
+9 -3
View File
@@ -5,11 +5,17 @@
<script>
export default {
onLoad() {
this.fnCheckHadStarted();
this.fnCheckShowStarted();
},
methods: {
// 检查是否已经启动
fnCheckHadStarted() {
// 检查是否需要跳转到启动
fnCheckShowStarted() {
if (!getApp().globalData.start.use) {
uni.switchTab({
url: '/pages/tabbar/home/home'
});
return;
}
if (uni.getStorageSync('APP_HAS_STARTED')) {
uni.switchTab({
url: '/pages/tabbar/home/home'
+15 -5
View File
@@ -1,11 +1,11 @@
<template>
<view class="app-page pb-24">
<!-- 博主信息 -->
<view class="blogger-info">
<view class="blogger-info" :style="[calcProfileStyle]">
<image class="avatar" :src="$utils.checkAvatarUrl(bloggerInfo.avatar)" mode="aspectFill" @click="miniProfileCard.show = true"></image>
<view class="profile">
<view class="author mt-24 text-size-g text-weight-b">{{ bloggerInfo.nickname }}</view>
<view class="desc mt-24 text-size-m">{{ bloggerInfo.description || '一个爱凑热闹的、喜欢捣鼓前端的博主。' }}</view>
<view class="desc mt-24 text-size-m">{{ bloggerInfo.description || '这个博主很懒,竟然没写介绍~' }}</view>
</view>
<image v-if="calcWaveUrl" :src="calcWaveUrl" mode="scaleToFill" class="gif-wave"></image>
</view>
@@ -168,7 +168,19 @@ export default {
},
computed: {
bloggerInfo() {
return this.$tm.vx.getters().blogger.getBlogger;
return this.$tm.vx.getters().getBlogger;
},
calcProfileStyle() {
let _imgUrlOr = getApp().globalData.aboutProfileImageUrl;
if (this.$utils.checkIsUrl(_imgUrlOr)) {
return {
backgroundImage: `url(${_imgUrlOr})`
};
} else {
return {
background: _imgUrlOr
};
}
},
calcWaveUrl() {
return getApp().globalData.waveImageUrl;
@@ -437,10 +449,8 @@ export default {
position: relative;
width: 100%;
height: 600rpx;
background-image: url('@/static/summer.jpg');
background-size: cover;
background-repeat: no-repeat;
// box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
&:before {
content: '';
width: 100%;
+16 -3
View File
@@ -23,6 +23,10 @@
@refresherrefresh="fnGetData(true)"
@scrolltolower="fnGetData(false)"
@scroll="fnOnScroll"
@touchmove.stop
@touchstart="fnOnTouchStart"
@touchend="fnOnTouchEnd"
@touchcancel="fnOnTouchEnd"
>
<view v-if="dataList.length == 0" class="article-empty flex flex-center">
<tm-empty icon="icon-shiliangzhinengduixiang-" label="该分类下暂无文章~"></tm-empty>
@@ -78,6 +82,8 @@ export default {
isLoadMore: false,
loadMoreText: '',
scrollTop: 0,
tempScrollTop: 0,
scrollTimeout: null,
triggered: false
};
},
@@ -181,9 +187,7 @@ export default {
});
},
fnOnScroll(e) {
throttle(() => {
this.scrollTop = e.detail.scrollTop;
}, 1000);
this.tempScrollTop = e.detail.scrollTop;
},
fnToTopScroll() {
uni.pageScrollTo({
@@ -191,6 +195,15 @@ export default {
duration: 500
});
this.scrollTop = 0;
this.tempScrollTop = 0;
},
fnOnTouchStart() {
clearTimeout(this.scrollTimeout);
},
fnOnTouchEnd() {
this.scrollTimeout = setTimeout(() => {
this.scrollTop = this.tempScrollTop;
}, 500);
}
}
};
+33 -22
View File
@@ -16,17 +16,17 @@
<tm-skeleton model="listAvatr"></tm-skeleton>
</view>
<!-- 内容区域 -->
<view class="content" v-else>
<view class="content" v-else :class="{ 'bg-white': dataList.length !== 0 }">
<view v-if="dataList.length == 0" class="content-empty">
<!-- 空布局 -->
<tm-empty icon="icon-shiliangzhinengduixiang-" label="该分类下暂无数据"></tm-empty>
<tm-empty icon="icon-shiliangzhinengduixiang-" label="博主还没有分享图片~"></tm-empty>
</view>
<block v-else>
<tm-flowLayout v-if="globalAppSettings.gallery.useWaterfull" @click="fnOnClick" ref="wafll">
<template v-slot:left="{ hdata }">
<tm-translate animation-name="fadeUp">
<view class="round-3 shadow-2 overflow white">
<tm-images :previmage="true" :src="hdata.item.image"></tm-images>
<tm-images :previmage="false" :src="hdata.item.image" @click="fnPreview(hdata.item)"></tm-images>
<view class="pa-10 text-size-s">
<view class="text-overflow-2">
<tm-tags color="bg-gradient-amber-accent" :shadow="0" :dense="true" size="s" model="fill">{{ hdata.item.team }}</tm-tags>
@@ -51,7 +51,7 @@
<template v-slot:right="{ hdata }">
<tm-translate animation-name="fadeUp">
<view class="round-3 shadow-2 overflow white">
<tm-images :previmage="true" :src="hdata.item.image"></tm-images>
<tm-images :previmage="false" :src="hdata.item.image" @click="fnPreview(hdata.item)"></tm-images>
<view class="pa-10 text-size-s">
<view class="text-overflow-2">
<tm-tags :shadow="0" :dense="true" color="bg-gradient-amber-accent" size="s" model="fill">{{ hdata.item.team }}</tm-tags>
@@ -227,26 +227,32 @@ export default {
this.$httpApi
.getPhotoListByPage(this.queryParams)
.then(res => {
this.loading = 'success';
this.result = res.data;
if (res.data.content.length != 0) {
const _list = res.data.content.map((item, index) => {
item['image'] = this.$utils.checkImageUrl(item.thumbnail);
item['model'] = 'text';
item['takeTime'] = this.$tm.dayjs(item['takeTime']).format('DD/MM/YYYY');
return item;
});
this.fnCacheDataList(_list);
if (this.globalAppSettings.gallery.useWaterfull) {
this.dataList = _list;
this.$nextTick(() => {
this.$refs.wafll.pushData(_list);
if (res.status == 200) {
this.loading = 'success';
this.result = res.data;
if (res.data.content.length != 0) {
const _list = res.data.content.map((item, index) => {
item['image'] = this.$utils.checkImageUrl(item.thumbnail);
item['model'] = 'text';
item['takeTime'] = this.$tm.dayjs(item['takeTime']).format('DD/MM/YYYY');
return item;
});
} else {
this.dataList = this.dataList.concat(_list);
this.fnCacheDataList(_list);
if (this.globalAppSettings.gallery.useWaterfull) {
this.dataList = _list;
this.$nextTick(() => {
this.$refs.wafll.pushData(_list);
});
} else {
this.dataList = this.dataList.concat(_list);
}
}
this.loadMoreText = res.data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
} else {
this.loading = 'error';
this.waterfall.loading = 'finish';
this.loadMoreText = '';
}
this.loadMoreText = res.data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
})
.catch(err => {
console.error(err);
@@ -276,6 +282,9 @@ export default {
},
// 预览
fnPreview(item, index) {
if (!index) {
index = this.dataList.findIndex(x => x.id == item.id);
}
uni.previewImage({
current: index,
urls: this.dataList.map(x => x.image),
@@ -287,7 +296,7 @@ export default {
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.app-page {
width: 100vw;
min-height: 100vh;
@@ -301,6 +310,7 @@ export default {
}
}
.content {
box-sizing: border-box;
padding: 0 24rpx;
padding-top: 24rpx;
@@ -312,6 +322,7 @@ export default {
}
}
.loading-wrap {
box-sizing: border-box;
padding: 24rpx;
}
</style>
+12 -14
View File
@@ -136,9 +136,8 @@ export default {
},
computed: {
// 获取博主信息
bloggerInfo() {
let blogger = this.$tm.vx.getters().blogger.getBlogger;
let blogger = this.$tm.vx.getters().getBlogger;
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
return blogger;
}
@@ -150,13 +149,6 @@ export default {
created() {
this.fnQuery();
this.$httpApi
.getOptionList()
.then(result => {
console.log('配置列表:');
console.log(result);
})
.catch(err => {});
},
onPullDownRefresh() {
this.isLoadMore = false;
@@ -177,7 +169,6 @@ export default {
}
},
methods: {
// 查询数据
fnQuery() {
this.fnGetBanner();
this.fnGetArticleList();
@@ -196,7 +187,10 @@ export default {
this.$httpApi
.getCategoryList({ more: true })
.then(res => {
this.categoryList = res.data;
this.categoryList = res.data.sort((a, b) => {
return b.postCount - a.postCount;
});
setTimeout(() => {
this.loading = 'success';
}, 500);
@@ -216,12 +210,16 @@ export default {
fnGetBanner() {
const _this = this;
const _format = function(list, type) {
return list.map(item => {
return list.map((item, index) => {
switch (type) {
case 'list':
return {
id: '',
src: item
id: index,
nickname: _this.bloggerInfo.nickname,
avatar: _this.bloggerInfo.avatar,
address: item.href || '',
title: item.title,
image: _this.$utils.checkImageUrl(item.thumbnail)
};
case 'article':
return {
+63 -50
View File
@@ -7,9 +7,9 @@
<tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton>
</view>
<block v-else>
<view v-else class="content" :class="{ 'bg-white': result.length !== 0 }">
<!-- 空数据 -->
<view v-if="result.length == 0" class="empty flex flex-center"><tm-empty icon="icon-shiliangzhinengduixiang-" label="啊偶,博主还没有朋友呢~"></tm-empty></view>
<view v-if="result.length == 0" class="content-empty flex flex-center"><tm-empty icon="icon-shiliangzhinengduixiang-" label="啊偶,博主还没有朋友呢~"></tm-empty></view>
<!-- 如果只有一个分组使用列表的形式 result.length == 1 -->
<view v-else-if="result.length == 1" class="flex flex-col pb-24">
@@ -28,44 +28,55 @@
</view>
</tm-translate>
</block>
<!-- 返回顶部 -->
<tm-flotbutton @click="fnToTopPage" size="m" icon="icon-angle-up"></tm-flotbutton>
</view>
<!-- 如果大于一个分组使用联系人的索引形式 result.length > 1 -->
<block v-else-if="result.length > 1">
<tm-quickIndex :list="result">
<template #cell="{ data }">
<!-- 艳丽版本 -->
<view v-if="!globalAppSettings.links.useSimple" class="info flex pa-36 border-b-1" @click="fnOnLinkEvent(data.item)">
<view class="link-logo">
<cache-image class="link-logo_img" radius="12rpx" :url="data.item.logo" :fileMd5="data.item.logo" mode="aspectFill"></cache-image>
</view>
<view class="flex flex-col pl-30 info-detail">
<view class="link-card_name text-size-l text-weight-b text-red">{{ data.item.name }}</view>
<view class="poup-tag ml--10 mt-6">
<tm-tags color="bg-gradient-amber-accent" :shadow="0" size="s" model="fill">ID{{ data.item.id }}</tm-tags>
<tm-tags color=" bg-gradient-light-blue-lighten" :shadow="0" size="s" model="fill">{{ data.item.team }}</tm-tags>
<block v-else>
<block v-for="(team, index) in result" :key="index">
<tm-translate animation-name="fadeUp" :wait="(index + 1) * 50">
<view class="grey-lighten-4 text text-size-s text-weight-b px-32 py-12">{{ team.title }}</view>
<block v-for="(link, linkIndex) in team.children" :key="link.id">
<tm-translate animation-name="fadeUp" :wait="(linkIndex + 1) * 50">
<!-- 色彩版本 -->
<view
v-if="!globalAppSettings.links.useSimple"
class="info flex pt-24 pb-24 pl-12 pr-12"
:class="{ 'border-b-1': linkIndex != team.children.length - 1 || index == result.length - 1 }"
@click="fnOnLinkEvent(link)"
>
<view class="link-logo">
<cache-image class="link-logo_img" radius="12rpx" :url="link.logo" :fileMd5="link.logo" mode="aspectFill"></cache-image>
</view>
<view class="flex flex-col pl-30 info-detail">
<view class="link-card_name text-size-l text-weight-b text-red">{{ link.name }}</view>
<view class="poup-tag ml--10 mt-6">
<tm-tags color="bg-gradient-amber-accent" :shadow="0" size="s" model="fill">ID{{ link.id }}</tm-tags>
<tm-tags color=" bg-gradient-light-blue-lighten" :shadow="0" size="s" model="fill">{{ link.team }}</tm-tags>
</view>
<view class="link-card_desc text-overflow text-size-s mt-4">博客简介{{ link.description || '这个博主很懒,没写简介~' }}</view>
</view>
</view>
<view class="link-card_desc text-overflow text-size-s mt-4">博客简介{{ data.item.description || '这个博主很懒,没写简介~' }}</view>
</view>
</view>
<!-- 简洁版本 -->
<view v-else class="link-card flex ml-24 mr-24 pt-24 pb-24" @click="fnOnLinkEvent(data.item)">
<image class="logo shadow-6" :src="data.item.logo" mode="aspectFill"></image>
<view class="info pl-24">
<view class="name text-size-g">{{ data.item.name }}</view>
<view class="desc mt-12 text-size-s text-grey-darken-1">{{ data.item.description }}</view>
<view v-if="false" class="link mt-12 text-size-m text-grey-darken-1">
<text class="iconfont icon-link mr-6 text-size-s"></text>
{{ data.item.url }}
<!-- 简洁版本 -->
<view v-else class="link-card flex ml-24 mr-24 pt-24 pb-24" @click="fnOnLinkEvent(link)">
<image class="logo shadow-6" :src="link.logo" mode="aspectFill"></image>
<view class="info pl-24">
<view class="name text-size-g">{{ link.name }}</view>
<view class="desc mt-12 text-size-s text-grey-darken-1">{{ link.description }}</view>
<view v-if="false" class="link mt-12 text-size-m text-grey-darken-1">
<text class="iconfont icon-link mr-6 text-size-s"></text>
{{ link.url }}
</view>
</view>
</view>
</view>
</view>
</template>
</tm-quickIndex>
</tm-translate>
</block>
</tm-translate>
</block>
</block>
<!-- 返回顶部 -->
<tm-flotbutton v-if="linkTotal > 10" color="light-blue" @click="fnToTopPage" size="m" icon="icon-angle-up"></tm-flotbutton>
<!-- 详情弹窗 -->
<tm-poup v-model="detail.show" :width="640" height="auto" position="center" :round="6">
<view class="poup pa-36">
@@ -90,7 +101,7 @@
<view class="mt-24"><tm-images :width="568" :round="2" :src="caclSiteThumbnail(detail.data.url)" mode="aspectFill"></tm-images></view>
</view>
</tm-poup>
</block>
</view>
</view>
</template>
@@ -101,11 +112,9 @@ import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vu
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue';
import tmQuickIndex from '@/tm-vuetify/components/tm-quickIndex/tm-quickIndex.vue';
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
import { GetRandomNumberByRange } from '@/utils/random.js';
import findCnChart from '@/tm-vuetify/tool/function/findCnChart.js';
export default {
components: {
tmSkeleton,
@@ -114,7 +123,6 @@ export default {
tmTags,
tmEmpty,
tmImages,
tmQuickIndex,
tmPoup
},
data() {
@@ -129,7 +137,8 @@ export default {
detail: {
show: false,
data: {}
}
},
linkTotal: 0
};
},
computed: {
@@ -158,6 +167,7 @@ export default {
return this.$haloConfig.colors[_r];
},
fnGetData() {
this.linkTotal = 0;
this.loading = 'loading';
uni.showLoading({
mask: true,
@@ -169,18 +179,16 @@ export default {
if (res.status == 200) {
console.log('请求结果:');
console.log(res);
let resData = res.data;
// 处理数据
const _result = resData.map(item => {
const _result = res.data.map(item => {
const _team = item.team || '未分组';
const _firstChart = _team ? _team.substring(0, 1) : '';
const _links = item.links.map(link => {
this.linkTotal += 1;
link.logo = this.$utils.checkAvatarUrl(link.logo);
return link;
});
return {
title: _team,
index: findCnChart(_firstChart),
children: _links
};
});
@@ -190,9 +198,7 @@ export default {
this.loading = 'success';
}, 500);
} else {
setTimeout(() => {
this.loading = 'error';
}, 500);
this.loading = 'error';
}
})
.catch(err => {
@@ -240,17 +246,24 @@ export default {
min-height: 100vh;
display: flex;
flex-direction: column;
background-color: #fafafd;
}
.loading-wrap {
padding: 24rpx;
min-height: 100vh;
}
.empty {
width: 100vw;
height: 60vh;
}
.content {
padding: 0 24rpx;
padding-top: 24rpx;
.content-empty {
height: 60vh;
display: flex;
align-items: center;
justify-content: center;
}
}
.link-card {
border-bottom: 2rpx solid #f5f5f5;
background-color: #ffffff;
@@ -266,7 +279,7 @@ export default {
// height: 126rpx;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
border-radius: 12rpx;
border: 6rpx solid #ffffff;
box-shadow: none;
}
+1 -1
View File
@@ -26,7 +26,7 @@
</tm-grouplist>
</view>
<view class="copyright bg-white text-size-xs text-align-center text-grey-darken-1 pa-36">
<view class="">根据 MIT 许可证发布</view>
<view class="">根据 AGPL-3.0 协议开源</view>
<view class="mt-8"> 2022 uni-halo 开源项目丨巷子工坊@小莫唐尼 </view>
</view>
</view>
+74 -18
View File
@@ -11,14 +11,11 @@
<view class="title">{{ result.title }}</view>
<view class="detail">
<view class="author">
<text class="author-name">{{ author.name }}</text>
<text class="author-time">发布于{{ { d: result.createTime, f: 'yyyy年MM月dd日 星期w' } | formatTime }}</text>
<text class="author-name">博主{{ author.nickname }}</text>
<text class="author-time">时间{{ { d: result.createTime, f: 'yyyy年MM月dd日 星期w' } | formatTime }}</text>
</view>
<view class="cover" v-if="result.thumbnail"><image class="cover-img" mode="aspectFill" :src="calcUrl(result.thumbnail)"></image></view>
<!-- <view class="mt-24">
<text class="category-tag" v-for="(item, index) in result.tags" :key="index">{{ item.name }}</text>
</view> -->
<view class="count" :class="{ 'no-thumbnail': !result.thumbnail }">
<view class="count-item">
<text class="value">{{ result.visits }}</text>
@@ -41,13 +38,21 @@
</view>
<!-- 分类 -->
<view class="category">
<view class="">
分类
<text class="category-tag" v-for="(item, index) in result.categories" :key="index">{{ item.name }}</text>
<view class="category-type">
<text class="text-weight-b">分类</text>
<text v-if="result.categories.length == 0" class="category-tag is-empty">未选择分类</text>
<block v-else>
<text class="category-tag" v-for="(item, index) in result.categories" :key="index" @click="fnToCate(item)">{{ item.name }}</text>
</block>
</view>
<view class="mt-18">
标签
<text class="category-tag" v-for="(item, index) in result.tags" :key="index">{{ item.name }}</text>
<view class="mt-18 category-type">
<text class="text-weight-b">标签</text>
<text v-if="result.tags.length == 0" class="category-tag is-empty">未选择标签</text>
<block v-else>
<text class="category-tag" :style="{ backgroundColor: item.color }" v-for="(item, index) in result.tags" :key="index" @click="fnToTag(item)">
{{ item.name }}
</text>
</block>
</view>
</view>
<!-- 广告区域 -->
@@ -88,10 +93,20 @@
<!-- #endif -->
</view>
<!-- 免责声明todo -->
<!-- 版权声明 -->
<view v-if="copyright.use" class="copyright-wrap bg-white mt-24 pa-24 round-4">
<view class="copyright-title text-weight-b">版权信息</view>
<view class="copyright-content mt-12 grey-lighten-5 text-grey-darken-2 round-4 pt-12 pb-12 pl-24 pr-24 ">
<view v-if="copyright.author" class="copyright-text text-size-s ">版权归属{{ copyright.author }}</view>
<view v-if="copyright.description" class="copyright-text text-size-s mt-12">版权说明{{ copyright.description }}</view>
<view v-if="copyright.violation" class="copyright-text text-size-s mt-12 text-red">侵权处理{{ copyright.violation }}</view>
</view>
</view>
<!-- 评论展示区域 -->
<view class="comment-wrap mt-24 pa-24 round-4"><commentList :postId="result.id" :post="result" @on-comment-detail="fnOnShowCommentDetail"></commentList></view>
<view class="comment-wrap bg-white mt-24 pa-24 round-4">
<commentList :disallowComment="result.disallowComment" :postId="result.id" :post="result" @on-comment-detail="fnOnShowCommentDetail"></commentList>
</view>
</view>
<!-- 返回顶部 -->
@@ -228,6 +243,9 @@ export default {
};
},
computed: {
copyright() {
return getApp().globalData.copyright;
},
calcUrl() {
return url => {
if (this.$utils.checkIsUrl(url)) {
@@ -238,7 +256,7 @@ export default {
},
// 获取博主信息
bloggerInfo() {
let blogger = this.$tm.vx.getters().blogger.getBlogger;
let blogger = this.$tm.vx.getters().getBlogger;
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
return blogger;
}
@@ -304,6 +322,9 @@ export default {
},
fnToComment() {
if (this.result.disallowComment) {
return uni.$tm.toast('文章已开启禁止评论!');
}
this.$Router.push({
path: '/pagesA/comment/comment',
query: {
@@ -598,6 +619,16 @@ export default {
.catch(err => {
this.commentDetail.loading = 'error';
});
},
fnToCate(category) {
uni.navigateTo({
url: `/pagesA/category-detail/category-detail?slug=${category.slug}&name=${category.name}`
});
},
fnToTag(tag) {
uni.navigateTo({
url: `/pagesA/tag-detail/tag-detail?id=${tag.id}&slug=${tag.slug}&name=${tag.name}`
});
}
}
};
@@ -646,7 +677,7 @@ export default {
&-name {
}
&-time {
margin-left: 12rpx;
margin-left: 36rpx;
}
}
@@ -711,12 +742,18 @@ export default {
box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
// border: 2rpx solid #f8f8f8;
font-size: 28rpx;
&-type {
line-height: 55rpx;
}
&-tag {
background-color: #5bb8fa;
color: #fff;
padding: 6rpx 12rpx;
border-radius: 6rpx;
font-size: 24rpx;
&.is-empty {
background-color: #607d8b;
}
}
}
.category-tag + .category-tag {
@@ -725,11 +762,27 @@ export default {
.content {
margin-top: 24rpx;
}
.ad-wrap {
.evan-markdown,
.ad-wrap,
.copyright-wrap,
.comment-wrap {
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.03);
}
.comment-wrap {
background-color: #fff;
.copyright-title {
position: relative;
box-sizing: border-box;
padding-left: 24rpx;
font-size: 30rpx;
&:before {
content: '';
position: absolute;
left: 0rpx;
top: 8rpx;
width: 8rpx;
height: 26rpx;
background-color: rgb(3, 174, 252);
border-radius: 6rpx;
}
}
.poup-head {
border-bottom: 2rpx solid #f5f5f5;
@@ -754,6 +807,9 @@ export default {
min-height: 60vh;
overflow: hidden;
}
.copyright-text {
line-height: 1.7;
}
.poster-loading {
width: 100%;
position: absolute;
+2 -2
View File
@@ -16,7 +16,7 @@
<view class="load-text">{{ loadMoreText }}</view>
</block>
<tm-flotbutton @click="fnToTopPage" size="m" icon="icon-angle-up"></tm-flotbutton>
<tm-flotbutton @click="fnToTopPage" color="light-blue" size="m" icon="icon-angle-up"></tm-flotbutton>
</block>
</view>
</template>
@@ -87,7 +87,7 @@ export default {
this.$httpApi
.getCategoryPostList(this.slug, this.queryParams)
.then(res => {
this.fnSetPageTitle(this.pageTitle + ` (共${res.data.total}篇)`);
this.fnSetPageTitle(`分类:${this.pageTitle} (共${res.data.total}篇)`);
this.result = res.data;
if (this.isLoadMore) {
this.dataList = this.dataList.concat(res.data.content);
+1 -1
View File
@@ -67,7 +67,7 @@ export default {
computed: {
// 评论游客信息
wxLoginVisitorUser() {
return uni.$tm.vx.getters().user.getWxLoginInfo;
return uni.$tm.vx.getters().getWxLoginInfo;
}
},
onLoad() {
+5 -21
View File
@@ -2,12 +2,9 @@
<view class="app-page flex flex-col felx-center bg-white">
<!-- 信息 -->
<view class="profile flex flex-col flex-center pa-36">
<view class="avatar">
<text v-if="false" class="icon flex flex-center"><text class="iconfont icon-check text-size-s"></text></text>
<image class="avatar-img" :src="bloggerInfo.avatar" mode="aspectFill"></image>
</view>
<view class="avatar"><image class="avatar-img" :src="bloggerInfo.avatar" mode="aspectFill"></image></view>
<view class="nickname mt-24 text-weight-b text-size-g">{{ bloggerInfo.nickname }}</view>
<view class="desc mt-24 text-size-m text-grey-darken-3">{{ bloggerInfo.description }}</view>
<view class="desc mt-24 text-size-m text-grey-darken-3">{{ bloggerInfo.description || '这个博主很懒,竟然没写介绍~' }}</view>
<!-- 联系图标 -->
<view class="mt-24 contact-icons">
<!-- 放全部似乎显得有点拥挤 -->
@@ -19,7 +16,7 @@
<text class="ml-12 halocoloricon halocoloricon-gitee"></text>
<text class="ml-12 halocoloricon halocoloricon-ic_email_round"></text>
</view>
<view class="mt-24 ">
<view v-if="false" class="mt-24 ">
<tm-button theme="bg-gradient-light-blue-accent" size="m" openType="contact">在线客服联系</tm-button>
<tm-button theme="bg-gradient-orange-accent" size="m" @click="fnOnToWeb">访问PC端博客</tm-button>
</view>
@@ -123,13 +120,13 @@ export default {
computed: {
// 获取博主信息
bloggerInfo() {
let blogger = this.$tm.vx.getters().blogger.getBlogger;
let blogger = this.$tm.vx.getters().getBlogger;
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
return blogger;
}
},
onLoad() {
this.fnSetPageTitle('');
this.fnSetPageTitle('联系博主');
},
created() {
this.fnGetData();
@@ -185,19 +182,6 @@ export default {
border: 6rpx solid #fff;
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.07);
overflow: hidden;
.icon {
width: 42rpx;
height: 42rpx;
position: absolute;
right: 6rpx;
bottom: 0rpx;
border-radius: 50%;
color: #fff;
box-sizing: border-box;
background-color: #03b0fd;
border: 4rpx solid #fff;
z-index: 2;
}
&-img {
width: 100%;
height: 100%;
+1 -1
View File
@@ -27,7 +27,7 @@
export default {
computed: {
bloggerInfo() {
return this.$tm.vx.getters().blogger.getBlogger;
return this.$tm.vx.getters().getBlogger;
}
}
};
+12 -5
View File
@@ -6,10 +6,10 @@
<tm-skeleton model="listAvatr"></tm-skeleton>
</view>
<!-- 内容区域 -->
<view v-else class="app-page-content bg-white pa-24">
<view v-else class="app-page-content pa-24" :class="{ 'bg-white': dataList.length !== 0 }">
<view v-if="dataList.length == 0" class="content-empty flex flex-center">
<!-- 空布局 -->
<tm-empty icon="icon-shiliangzhinengduixiang-" label="该分类下暂无数据"></tm-empty>
<tm-empty icon="icon-shiliangzhinengduixiang-" label="啊偶,博主还没有留言~"></tm-empty>
</view>
<block v-else>
<block v-for="(item, index) in dataList" :key="index">
@@ -28,9 +28,9 @@
</tm-translate>
</block>
<tm-flotbutton :offset="[16, 80]" @click="fnToTopPage" size="m" color="light-blue" icon="icon-angle-up"></tm-flotbutton>
<tm-flotbutton actions-pos="left" :show-text="true" color="bg-gradient-orange-accent" @click="fnToComment(null)"></tm-flotbutton>
<view class="load-text">{{ loadMoreText }}</view>
</block>
<tm-flotbutton actions-pos="left" :show-text="true" color="bg-gradient-orange-accent" @click="fnToComment(null)"></tm-flotbutton>
</view>
<!-- 评论详情 -->
@@ -77,7 +77,7 @@
</template>
<script>
import AppKeys from '@/config//keys.js';
import AppKeys from '@/config/keys.js';
import SheetConfig from '@/config/sheets.config.js';
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
@@ -164,6 +164,7 @@ export default {
.then(res => {
if (res.status == 200) {
this.loading = 'success';
// return;
this.loadMoreText = res.data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
// 处理数据
this.result = res.data;
@@ -252,12 +253,18 @@ export default {
width: 100vw;
display: flex;
flex-direction: column;
background-color: #fafafd;
}
.app-page-content {
box-sizing: border-box;
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.05);
// box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.05);
}
.content-empty {
width: 100%;
height: 60vh;
}
.loading-wrap {
box-sizing: border-box;
padding: 24rpx;
}
</style>
+39 -22
View File
@@ -9,8 +9,8 @@
<view class="sheet-content">内容区域</view>
</tm-sheet> -->
<tm-sheet :shadow="6" :padding="[12, 24]" :margin="[24, 24]">
<view class="py-12 px-24 mx-12 round-3 border-b-1 grey text">
<text class="text-size-n text-weight-b ">布局</text>
<view class="py-12 px-24 mx-12 round-3 grey text">
<text class="text-size-n text-weight-b text-grey-darken-3">布局</text>
<text class="text-grey text-size-xs px-10 ml-12">应用以及文章列表布局设置</text>
</view>
<view class="sheet-content">
@@ -54,7 +54,7 @@
</tm-sheet>
<tm-sheet v-if="false" :shadow="24" :padding="[12, 24]" :margin="[24, 24]">
<view class="py-12 px-24 mx-12 round-3 border-b-1 grey text">
<view class="py-12 px-24 mx-12 round-3 grey text">
<text class="text-size-n text-weight-b ">外观</text>
<text class="text-grey text-size-xs px-10">设置应用主题色/暗黑模式等</text>
</view>
@@ -65,19 +65,11 @@
</tm-sheet>
<tm-sheet :shadow="6" :padding="[12, 24]" :margin="[24, 24]">
<view class="py-12 px-24 mx-12 round-3 border-b-1 grey text">
<text class="text-size-n text-weight-b ">功能</text>
<view class="py-12 px-24 mx-12 round-3 grey text">
<text class="text-size-n text-weight-b text-grey-darken-3">功能</text>
<text class="text-grey text-size-xs px-10 ml-12">一些常用的功能性设置</text>
</view>
<view class="sheet-content">
<view class="mx-32 my-24 border-b-1 pb-24 flex-between">
<text class="text-size-m">显示完整统计</text>
<tm-switch v-model="appSettings.about.showAllCount" color="light-blue" :text="['是', '否']"></tm-switch>
</view>
<view class="mx-32 my-24 border-b-1 pb-24 flex-between">
<text class="text-size-m ">链接直接打开</text>
<tm-switch v-model="appSettings.contact.isLinkCopy" color="light-blue" :text="['是', '否']"></tm-switch>
</view>
<view class="mx-32 my-24 border-b-1 pb-24 flex-between">
<text class="text-size-m ">图库瀑布流模式</text>
<tm-switch v-model="appSettings.gallery.useWaterfull" color="light-blue" :text="['是', '否']"></tm-switch>
@@ -86,6 +78,10 @@
<text class="text-size-m ">友链简洁模式</text>
<tm-switch v-model="appSettings.links.useSimple" color="light-blue" :text="['是', '否']"></tm-switch>
</view>
<view class="mx-32 my-24 border-b-1 pb-24 flex-between">
<text class="text-size-m">是否圆形头像</text>
<tm-switch v-model="appSettings.isAvatarRadius" color="light-blue" :text="['是', '否']"></tm-switch>
</view>
<view class="mx-32 my-24 border-b-1 pb-24 flex-between">
<text class="text-size-m ">轮播图指示器</text>
<tm-switch v-model="appSettings.banner.useDot" color="light-blue" :text="['是', '否']"></tm-switch>
@@ -104,6 +100,15 @@
></tm-radio>
</tm-groupradio>
</view>
<view class="mx-32 my-24 border-b-1 pb-24 flex-between">
<text class="text-size-m">显示完整统计</text>
<tm-switch v-model="appSettings.about.showAllCount" color="light-blue" :text="['是', '否']"></tm-switch>
</view>
<view class="mx-32 my-24 border-b-1 pb-24 flex-between">
<text class="text-size-m ">链接直接打开</text>
<tm-switch v-model="appSettings.contact.isLinkCopy" color="light-blue" :text="['是', '否']"></tm-switch>
</view>
<view class="mx-32 my-24 border-b-1 pb-24 flex-between">
<text class="text-size-m ">显示后台入口</text>
<tm-switch v-model="appSettings.about.showAdmin" color="light-blue" :text="['是', '否']"></tm-switch>
@@ -148,6 +153,8 @@ export default {
isBlackTheme: false,
loading: true,
appSettings: {},
isSaved: true,
firstLoad: true,
homeLayout: {
list: [{ name: '一行一列', value: 'h_row_col1' }, { name: '一行两列', value: 'h_row_col2' }],
selectDefault: ['一行一列'],
@@ -169,15 +176,17 @@ export default {
dotPositionList: [{ name: '右边', value: 'right', checked: true }, { name: '下边', value: 'bottom', checked: false }]
};
},
computed: {
// 获取设置
_appSettings() {
return uni.$tm.vx.getters().setting.getSettings;
}
},
watch: {
_appSettings(val) {
this.appSettings = val;
appSettings: {
deep: true,
handler() {
if (this.firstLoad) {
this.firstLoad = false;
} else {
this.isSaved = false;
}
}
}
},
@@ -185,7 +194,7 @@ export default {
this.fnSetPageTitle('应用设置');
},
created() {
this.appSettings = this._appSettings;
this.appSettings = Object.assign({}, _DefaultAppSettings, uni.$tm.vx.getters().getSettings);
this.fnHandleFormatSelect();
uni.showLoading({
title: '加载中...',
@@ -242,6 +251,8 @@ export default {
},
// 保存
fnOnSave() {
this.isSaved = true;
console.log('this.appSettings', this.appSettings);
this.$tm.vx.commit('setting/setSettings', this.appSettings);
uni.$tm.toast('保存成功,部分设置在重启后生效!');
},
@@ -257,12 +268,17 @@ export default {
confirmColor: '#03a9f4'
})
.then(res => {
this.isSaved = true;
uni.$tm.vx.actions('setting/updateDefaultAppSettings');
uni.$tm.toast('系统设置已恢复为默认配置,部分设置在重启后生效!');
})
.catch(err => {});
},
fnOnBack() {
if (this.isSaved) {
uni.navigateBack();
return;
}
uni.$eShowModal({
title: '提示',
content: '您当前可能有未保存的数据,确定返回吗?',
@@ -274,6 +290,7 @@ export default {
})
.then(res => {
uni.navigateBack();
this.isSaved = true;
})
.catch(err => {});
}
+22 -8
View File
@@ -1,6 +1,5 @@
<template>
<view class="app-page bg-gradient-blue-lighten-b">
<!-- 流星-->
<view class="app-page bg-gradient-blue-lighten-b" :style="pageStyle">
<view class="tn-satr">
<view class="sky"></view>
<view class="stars">
@@ -41,18 +40,19 @@
</view>
</view>
<!-- 头像用户信息 -->
<view class="user-info__container flex flex-col flex-center">
<image class="user-info__avatar" src="https://blog-oss.925i.cn/blog-files/d500acd35e8b6d24bbfa2dabd2a605e6.png" mode="aspectFill"></image>
<view class="user-info__nick-name"> uni-halo </view>
<image class="user-info__avatar" :src="startInfo.logo" mode="aspectFill"></image>
<view class="user-info__nick-name"> {{ startInfo.title }} </view>
</view>
<view class="text-align-center text-white" style="padding: 60vh 0 0 0;">
<view class="" style="font-size: 44rpx;">全新UI准备出发</view>
<view class="mt-30 text-size-m">新触动 新感受 新体验</view>
<view class="" style="font-size: 44rpx;" v-if="startInfo.desc1">{{ startInfo.desc1 }}</view>
<view class="mt-30 text-size-m" v-if="startInfo.desc2">{{ startInfo.desc2 }}</view>
</view>
<view class="" style="padding: 120rpx 200rpx;z-index: 999;position: relative;"><view class="start-btn" @click="fnStart()">全新出发</view></view>
<view class="" style="padding: 120rpx 200rpx;z-index: 999;position: relative;">
<view class="start-btn" @click="fnStart()">{{ startInfo.btnText || '开始体验' }}</view>
</view>
<!-- 波浪效果 -->
<wave></wave>
@@ -63,6 +63,20 @@
import wave from '@/components/wave/wave.vue';
export default {
components: { wave },
computed: {
startInfo() {
return getApp().globalData.start;
},
pageStyle() {
if (this.startInfo.bg) {
const _bg = this.$utils.checkIsUrl(this.startInfo.bg) ? `url(${this.startInfo.bg})` : this.startInfo.bg;
return {
background: _bg + '!important'
};
}
return {};
}
},
methods: {
fnStart() {
uni.switchTab({
+144
View File
@@ -0,0 +1,144 @@
<template>
<view class="app-page">
<view v-if="loading != 'success'" class="loading-wrap">
<tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton>
</view>
<block v-else>
<view class="empty" v-if="dataList.length == 0"><tm-empty icon="icon-shiliangzhinengduixiang-" label="该标签下暂无文章"></tm-empty></view>
<block v-else>
<block v-for="(article, index) in dataList" :key="article.createTime">
<!-- 文章卡片 -->
<tm-translate animation-name="fadeUp" :wait="(index + 1) * 50"><article-card :article="article" @on-click="fnToArticleDetail"></article-card></tm-translate>
</block>
<view class="load-text">{{ loadMoreText }}</view>
</block>
<tm-flotbutton @click="fnToTopPage" color="light-blue" size="m" icon="icon-angle-up"></tm-flotbutton>
</block>
</view>
</template>
<script>
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
import ArticleCard from '@/components/article-card/article-card.vue';
export default {
components: {
tmSkeleton,
tmTranslate,
tmFlotbutton,
tmEmpty,
ArticleCard
},
data() {
return {
loading: 'loading',
queryParams: {
size: 10,
page: 0
},
slug: '',
pageTitle: '加载中...',
result: null,
dataList: [],
isLoadMore: false,
loadMoreText: ''
};
},
onLoad(e) {
this.slug = e.slug;
this.pageTitle = e.name;
this.fnGetData();
},
onPullDownRefresh() {
this.isLoadMore = false;
this.queryParams.page = 0;
this.fnGetData();
},
onReachBottom(e) {
if (this.result.hasNext) {
this.queryParams.page += 1;
this.isLoadMore = true;
this.fnGetData();
} else {
uni.showToast({
icon: 'none',
title: '没有更多数据了'
});
}
},
methods: {
fnGetData() {
uni.showLoading({
mask: true,
title: '加载中...'
});
// 设置状态为加载中
if (!this.isLoadMore) {
this.loading = 'loading';
}
this.loadMoreText = '加载中...';
this.$httpApi
.getTagPostsList(this.slug, this.queryParams)
.then(res => {
this.fnSetPageTitle(`标签:${this.pageTitle} (共${res.data.total}篇)`);
this.result = res.data;
if (this.isLoadMore) {
this.dataList = this.dataList.concat(res.data.content);
} else {
this.dataList = res.data.content;
}
this.loadMoreText = res.data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
setTimeout(() => {
this.loading = 'success';
}, 500);
})
.catch(err => {
console.error(err);
this.loading = 'error';
this.loadMoreText = '加载失败,请下拉刷新!';
})
.finally(() => {
setTimeout(() => {
uni.hideLoading();
uni.stopPullDownRefresh();
}, 500);
});
},
//跳转文章详情
fnToArticleDetail(article) {
uni.navigateTo({
url: '/pagesA/article-detail/article-detail?articleId=' + article.id,
animationType: 'slide-in-right'
});
}
}
};
</script>
<style lang="scss">
.app-page {
width: 100vw;
min-height: 100vh;
display: flex;
flex-direction: column;
padding: 24rpx 0;
background-color: #fafafd;
}
.loading-wrap {
padding: 0 24rpx;
min-height: 100vh;
}
.empty {
height: 60vh;
display: flex;
align-items: center;
justify-content: center;
}
</style>
+14
View File
@@ -0,0 +1,14 @@
<template>
<view>文章标签页面</view>
</template>
<script>
export default {
data() {
return {};
},
methods: {}
};
</script>
<style></style>
+1 -2
View File
@@ -146,13 +146,12 @@ export default {
},
computed: {
bloggerInfo() {
return this.$tm.vx.getters().blogger.getBlogger;
return this.$tm.vx.getters().getBlogger;
}
},
created() {
// this.fnGetData();
this.fnSetPageTitle('欢迎登录工作台');
console.log(this.bloggerInfo);
},
onPullDownRefresh() {
uni.stopPullDownRefresh();
+7 -5
View File
@@ -151,7 +151,8 @@ export default {
form: {
content: '',
keepRaw: true,
sourceContent: '',
originalContent: '',
formatContent: '',
type: 'PUBLIC'
},
modal: null,
@@ -532,16 +533,17 @@ export default {
return uni.$tm.toast('请输入内容!');
}
this.form.content = _content;
this.form.sourceContent = this.$refs.markdown.getText();
this.form.formatContent = _content;
this.form.originalContent = this.$refs.markdown.getText();
uni.setStorageSync('posts-content', _content);
uni.setStorageSync('posts-content-source', this.form.sourceContent);
uni.setStorageSync('posts-content-source', this.form.originalContent);
if (this.form.id) {
this.$Router.push({
path: '/pagesB/articles/article-setting',
query: { postsId: this.form.id, postTitle: this.form.title, isEdit: true, from: 'edit' }
query: { postsId: this.form.id, postTitle: this.form.title, isEdit: 1, from: 'edit' }
});
} else {
this.$Router.push({ path: '/pagesB/articles/article-setting', query: { postsId: '', postTitle: '', isEdit: false, from: 'edit' } });
this.$Router.push({ path: '/pagesB/articles/article-setting', query: { postsId: '', postTitle: '', isEdit: 0, from: 'edit' } });
}
return;
+26 -23
View File
@@ -60,9 +60,7 @@
<view>
<tm-groupcheckbox name="categoryIds">
<tm-checkbox :dense="true" :name="item.id" v-for="(item, index) in categories" :key="index" v-model="item.checked">
<template v-slot:default="{ checkData }">
<tm-button :flat="true" size="s" :theme="checkData.checked ? 'light-blue' : 'grey-lighten-4'" :plan="true">{{ item.name }}</tm-button>
</template>
<tm-button v-slot:default :flat="true" size="s" :theme="item.checked ? 'light-blue' : 'grey-lighten-4'" :plan="true">{{ item.name }}</tm-button>
</tm-checkbox>
</tm-groupcheckbox>
</view>
@@ -76,9 +74,7 @@
<view>
<tm-groupcheckbox name="tagIds">
<tm-checkbox :dense="true" :name="item.id" v-for="(item, index) in tags" :key="index" v-model="item.checked">
<template v-slot:default="{ checkData }">
<tm-button :flat="true" size="s" :theme="checkData.checked ? 'light-blue' : 'grey-lighten-4'" :plan="true">{{ item.name }}</tm-button>
</template>
<tm-button v-slot:default :flat="true" size="s" :theme="item.checked ? 'light-blue' : 'grey-lighten-4'" :plan="true">{{ item.name }}</tm-button>
</tm-checkbox>
</tm-groupcheckbox>
</view>
@@ -151,7 +147,7 @@
<!-- 操作区域 -->
<view class="btn-bar flex flex-center bg-white">
<tm-button theme="blue" :shadow="0" block :height="70" @click="fnOnSave(false, false)">立即保存</tm-button>
<tm-button theme="light-blue" :shadow="0" block :height="70" @click="fnOnSave(false, true)">保存并返回</tm-button>
<tm-button v-if="isEdit" theme="light-blue" :shadow="0" block :height="70" @click="fnOnSave(false, true)">保存并返回</tm-button>
<!-- <block v-if="from == 'edit' && isEdit">
<tm-button
v-if="article.status == 'DRAFT' || article.status == 'INTIMATE' || article.status == 'RECYCLE'"
@@ -357,10 +353,21 @@ export default {
list: ['常规', '高级', 'SEO']
},
article: {
title: '',
slug: '',
status: '',
content: '',
keepRaw: true,
sourceContent: '',
type: 'PUBLIC'
topPriority: 0,
summary: '',
password: '',
originalContent: '',
metaDescription: '',
formatContent: '',
editorType: 'MARKDOWN',
createTime: '',
categoryIds: [],
tagIds: []
},
articleStatus: {
list: [{ name: '发布', value: 'PUBLISHED' }, { name: '私有', value: 'INTIMATE' }, { name: '草稿', value: 'DRAFT' }, { name: '回收站', value: 'RECYCLE' }],
@@ -393,7 +400,7 @@ export default {
const from = this.$Route.query.from;
this.postsId = postsId;
this.postTitle = postTitle || '';
this.isEdit = isEdit;
this.isEdit = isEdit == 1 ? true : false;
this.from = from;
this.createTime = uni.$tm.dayjs(new Date().getTime()).format('YYYY-MM-DD HH:mm:ss');
this.fnGetSettings();
@@ -533,7 +540,7 @@ export default {
}
this.createTime = uni.$tm.dayjs(new Date(this.article.createTime).getTime()).format('YYYY-MM-DD HH:mm:ss');
if (this.postTitle) {
this.article.title = this.postTitle;
this.$set(this.article, 'title', this.postTitle);
}
}
const _cateRes = res[1];
@@ -626,7 +633,8 @@ export default {
// 设置文章内容
if (this.from == 'edit') {
this.article.content = uni.getStorageSync('posts-content');
this.article.sourceContent = uni.getStorageSync('posts-content-source');
this.article.formatContent = uni.getStorageSync('posts-content');
this.article.originalContent = uni.getStorageSync('posts-content-source');
}
if (this.articleStatus.selectValue != 'INTIMATE') {
this.article.password = '';
@@ -651,8 +659,7 @@ export default {
delta: 1
});
}
uni.$emit('refresh-article-list');
// uni.$emit('refresh-article-list');
}, 1000);
} else {
uni.$tm.toast('保存失败,请重试!');
@@ -662,13 +669,12 @@ export default {
}
})
.catch(err => {
uni.$tm.toast('保存失败,请重试!');
uni.$tm.toast(`保存失败,${err.message}!`);
if (isChangeStatus) {
this.article.status = this.article.status == 'DRAFT' ? 'PUBLISHED' : 'DRAFT';
}
});
} else {
this.article.content = this.article.sourceContent = uni.getStorageInfoSync();
this.$httpApi.admin
.createPosts(this.article)
.then(res => {
@@ -678,12 +684,9 @@ export default {
uni.setStorageSync('posts-content', '');
uni.setStorageSync('posts-content-source', '');
setTimeout(() => {
if (isBack) {
uni.navigateBack({
delta: 1
});
}
uni.navigateBack({
delta: 2
});
uni.$emit('refresh-article-list');
}, 1000);
} else {
@@ -691,7 +694,7 @@ export default {
}
})
.catch(err => {
uni.$tm.toast('发布失败,请重试!');
uni.$tm.toast(`发布失败:${err.message}`);
});
}
},
+1 -1
View File
@@ -306,7 +306,7 @@ export default {
},
// 设置文章信息
fnOnSetArticle(article, index) {
this.$Router.push({ path: '/pagesB/articles/article-setting', query: { postsId: article.id, postTitle: article.title, isEdit: true, from: 'list' } });
this.$Router.push({ path: '/pagesB/articles/article-setting', query: { postsId: article.id, postTitle: article.title, isEdit: 1, from: 'list' } });
},
// 删除文章
fnOnDelArticle(article, index) {
+1 -2
View File
@@ -185,7 +185,7 @@ export default {
computed: {
// 获取博主信息
bloggerInfo() {
return this.$tm.vx.getters().blogger.getBlogger;
return this.$tm.vx.getters().getBlogger;
}
},
onLoad() {
@@ -193,7 +193,6 @@ export default {
},
created() {
this.fnGetData();
console.log(this.bloggerInfo);
},
onPullDownRefresh() {
this.isLoadMore = false;
+1 -1
View File
@@ -177,7 +177,7 @@ export default {
},
computed: {
journalInfo() {
return uni.$tm.vx.getters().journal.getJournalInfo;
return uni.$tm.vx.getters().getJournalInfo;
}
},
onLoad() {
+1 -1
View File
@@ -85,7 +85,7 @@ export default {
},
computed: {
bloggerInfo() {
return this.$tm.vx.getters().blogger.getBlogger;
return this.$tm.vx.getters().getBlogger;
},
showCopyright() {
return getApp().globalData.showCopyright;
+1 -1
View File
@@ -97,7 +97,7 @@ export default {
},
onLoad() {
this.fnSetPageTitle('我的个人信息');
this.fnSetPageTitle('个人资料');
},
created() {
this.fnGetData();
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

+12 -12
View File
@@ -10,6 +10,7 @@
* 修改时间:
*/
import Blogger from '@/api/blogger.js'
import HaloConfig from '@/config/halo.config.js';
export default {
state: {
blogger: {},
@@ -25,18 +26,17 @@ export default {
},
},
actions: {
//通过actions提交更新数据
fnGetBlogger({
commit
}) {
Blogger.getBloggerInfo().then((res) => {
console.log('获取博主信息成功:');
console.log(res);
commit("setBlogger", res.data);
}).catch((err) => {
console.log('哈哈,获取博主信息失败啦!');
console.log(err);
});
fnGetBlogger(context) {
if (HaloConfig.author.use) {
context.commit("setBlogger", HaloConfig.author);
} else {
Blogger.getBloggerInfo().then((res) => {
context.commit("setBlogger", res.data);
}).catch((err) => {
// 如果失败,则加载默认配置信息
context.commit("setBlogger", HaloConfig.author);
});
}
},
},
};
+2 -6
View File
@@ -24,17 +24,13 @@ export default {
mutations: {
setSettings(state, data) {
state.settings = data;
console.log('设置:', data);
setAppSettings(data)
},
},
actions: {
// 设置默认的数据
updateDefaultAppSettings({
commit
}) {
console.log(_DefaultAppSettings);
commit('setSettings', JSON.parse(JSON.stringify(_DefaultAppSettings)))
updateDefaultAppSettings(context) {
context.commit('setSettings', JSON.parse(JSON.stringify(_DefaultAppSettings)))
},
// 检查并设置默认的数据
checkAndSetDefaultAppSettings(context) {
+4 -10
View File
@@ -45,14 +45,11 @@ export default {
},
},
actions: {
// 登录
adminLogin({
commit
}, data) {
adminLogin(context, data) {
return new Promise((resolve, reject) => {
User.login(data).then((res) => {
if (res.status == 200) {
commit("setAdminToken", res.data);
context.commit("setAdminToken", res.data);
resolve(res)
} else {
reject(err)
@@ -72,11 +69,8 @@ export default {
})
}
},
// 登出
adminLogout({
commit
}) {
commit("setAdminToken", null);
adminLogout(context) {
context.commit("setAdminToken", null);
}
},
};
+20
View File
@@ -1,3 +1,23 @@
## 2.0.12022-12-8
* 修复 tm-icons在小程序下可能图标与文字无法对齐
* 优化 tm-album性能,修复可能在嵌套组件中,无法计算宽高信息.
* 优化 tm-dragList性能
* 优化 tm-row性能
* 优化 tm-segTabs性能
* 优化 tm-sheet性能
* 优化 tm-slider,tm-sliders性能
* 优化 tm-more性能
* 优化 tm-propress性能,修复可能在嵌套组件中,无法计算宽高信息.
* 优化 tm-dialogtm-poup,动效及样式
* 微调 tm-button
* 美化了tm-calendartm-calendar-view,选中改成圆形,范围改成椭圆形。
* 修复 tm-countdown 完成的时机逻辑有误,修改后默认为完成状态。
* 增强 tm-upload 新增showSort属性,用于上传的文件排序功能。
* 修复 tm-swiper圆角失效。
* 修复 tm-weekbar日期问题。
* 修复 tm-dragGrid 动态修改数据后的判断逻辑问题。
* 优化 tm-grouplist 渲染性能,现在已经是秒渲染。
* 修复$tm.vx.getters()下的属性丢失。
## 2.0.02022-6-7
* tm-card修复点击操作按钮时返回的参数非index,而是项目标题。修复动态配置一些属性不生效的问题。
* tm-dropDownMenu 优化,当重置项时,默认将使用你初始的选中数据来进行填充,默认选中。
+14 -15
View File
@@ -159,7 +159,10 @@
}
},
async mounted() {
await this.setInits();
this.setInits();
},
updated() {
this.setInits();
},
methods: {
//处理数据,以标准化。
@@ -186,20 +189,16 @@
async setInits(){
this.$nextTick(async function() {
let t = this;
this.$tm.sleep(150).then(async function(){
let p = await t.$Querey('.tm-album',t).catch(e =>{});
if (!p[0]) return;
let grid = t.grid || 1;
let size = (p[0].width+t.gutter)/grid;
let gutter = t.gutter*2
let ratio = 750/uni.upx2px(750);
let blv = size * ratio - gutter;
t.alb_body_size = size * ratio;
t.alb_wk_body_size = p[0].width*ratio+t.gutter*2+(grid*2);
t.alb_size = blv;
})
let p = await t.$Querey('.tm-album',t,0).catch(e =>{});
if (!p[0]) return;
let grid = t.grid || 1;
let size = (p[0].width+t.gutter)/grid;
let gutter = t.gutter*2
let ratio = 750/uni.upx2px(750);
let blv = size * ratio - gutter;
t.alb_body_size = size * ratio;
t.alb_wk_body_size = p[0].width*ratio+t.gutter*2+(grid*2);
t.alb_size = blv;
});
},
@@ -1,5 +1,5 @@
<template>
<view class="tm-button " :class="[block ? 'd-block' : 'd-inline-block ']">
<view class="tm-button " :class="[block ? 'd-block' : 'd-inline-block ']" hover-class="opacity-6">
<view
class="flex-center tm-button-btn fulled-height"
:class="[block ? '' : customDense_puted ? '' : 'ma-10', block ? 'd-block' : 'd-inline-block', black_tmeme ? 'bk' : '', customStyleTm]"
@@ -54,7 +54,7 @@
<block v-if="vtype == true">
<text
v-if="!fab && icon"
:class="[`${prefx_computed} ${icon}`, fontColor ? `text-${colors.color}` : '', black_tmeme ? 'opacity-6' : '', 'px-12']"
:class="[`${prefx_computed} ${icon}`, fontColor ? `text-${colors.color}` : '', black_tmeme ? 'opacity-6' : '', 'px-12','flex-shrink']"
:style="{
fontSize: `${icon_size.px}px`,
lineHeight:'normal'
@@ -62,7 +62,7 @@
></text>
<text
v-if="fab && icon && !loading && !titl"
:class="[`${prefx_computed} ${icon}`, fontColor ? `text-${colors.color}` : '', black_tmeme ? 'opacity-6' : '']"
:class="[`${prefx_computed} ${icon}`, fontColor ? `text-${colors.color}` : '', black_tmeme ? 'opacity-6' : '','flex-shrink']"
:style="{
fontSize: `${icon_size.px}px`,
lineHeight:'normal'
@@ -70,7 +70,7 @@
></text>
<text
v-if="fab && icon && !loading && titl"
:class="[`${prefx_computed} ${icon}`, fontColor ? `text-${color_tmeme}` : '', black_tmeme ? 'opacity-6' : '']"
:class="[`${prefx_computed} ${icon}`, fontColor ? `text-${color_tmeme}` : '', black_tmeme ? 'opacity-6' : '','flex-shrink']"
:style="{
fontSize: `${icon_size.px}px`,
lineHeight:'normal'
@@ -80,7 +80,7 @@
<block v-if="vtype == false"><tm-icons :size="icon_size.upx" :name="icon"></tm-icons></block>
</slot>
<view v-if="!fab || showValue" class="d-inline-block tm-button-label flex-shrink" :style="{ fontSize: font_size }" :class="[fontColor ? `text-${colors.color}` : '']">
<view v-if="!fab || showValue" class="d-inline-block tm-button-label flex-shrink" :style="{ fontSize: font_size }" :class="[fontColor ? `text-${colors.color}` : '',vertical ? 'full ' : '',]">
<slot name="default" :data="label">{{ label }}</slot>
</view>
</button>
@@ -556,11 +556,13 @@ export default {
line-height: 88upx;
height: 88upx;
vertical-align: middle;
// #ifdef H5
transition: all 0.3s;
// #endif
.tm-button-label {
vertical-align: middle;
}
&::after {
border: none;
@@ -671,6 +673,7 @@ export default {
}
&.showValue {
line-height: inherit !important;
padding: 0 !important;
}
&.noGutter {
@@ -54,13 +54,21 @@
:key="index+'_acv'"><text class="text-size-s py-16">{{item}}</text>
</tm-col>
<!-- #endif -->
<tm-col :round="(item.start||item.end||item.checked)?4:0" @click="day_danxuanclick(item,index)"
:color="item.beginEnd?(item.checked===true||item.start||item.end?color_tmeme+(black_tmeme?' bk ':''):(item.guocheng?color_tmeme+' text opacity-7 '+(black_tmeme?'bk':''):'')):''"
<tm-col
@click="day_danxuanclick(item,index)"
color=""
:custom-class="isSelectedDateClass(item)"
align="middle" width="14.28%" v-for="(item,index) in nowData"
:key="index">
<view class="tm-calendar-col flex-center flex-col py-4" :class="[black_tmeme&&!item.beginEnd?' opacity-2 ':'']">
justify="center" width="14.28%"
v-for="(item,index) in nowData"
:key="index"
>
<view class="tm-calendar-col flex-center flex-col "
:style="[mode=='rang'?{width:'100%',height:'90rpx'}:{width:'90rpx',height:'90rpx',overflow: 'hidden','border-radius': '50% !important'}]"
:class="[
item.start?'round-l-24':'',
item.end?'round-r-24':'',
item.beginEnd?(item.checked===true||item.start||item.end?color_tmeme+(black_tmeme?' bk ':''):(item.guocheng?color_tmeme+' text opacity-7 '+(black_tmeme?'bk':''):'')):'',
black_tmeme&&!item.beginEnd?' opacity-2 ':'']">
<text class="text-size-n "
:class="[
@@ -70,19 +78,14 @@
!item.beginEnd?'text-grey-lighten-3':''
]"
>{{item.day}}</text>
<!--
!item.nowMonth&&!item.guocheng&&!item.checked&&!item.start&&!item.end?(black?'text-grey-darken-3':'text-grey-lighten-1'):'',
item.checked||item.start||item.end?'text-white':(item.guocheng?'':'text-grey-lighten-1'),
item.guocheng?'text-'+color:'',
!item.beginEnd?'text-grey-lighten-3':'',
-->
<view class="text-size-xs text_bl"
>
<block v-if="item.start">
</block>
<block v-if="item.end">
结束
-止
</block>
<block v-if="!item.start&&!item.end">
@@ -134,6 +137,11 @@
import tmRow from "@/tm-vuetify/components/tm-row/tm-row.vue"
import tmButton from "@/tm-vuetify/components/tm-button/tm-button.vue"
import tmPoup from "@/tm-vuetify/components/tm-poup/tm-poup.vue"
import * as dayjs from "@/tm-vuetify/tool/function/dayjs/dayjs.min.js"
const calendar = require("@/tm-vuetify/tool/function/dayjs/calendar.js")
dayjs.extend(calendar)
const DayJs = dayjs.default
export default {
components:{tmIcons,tmCol,tmRow,tmButton,tmPoup},
name: "tm-calendar",
@@ -262,7 +270,7 @@
if(!this.cal) return;
// 自动更新默认的显示时间。
if(this.mode=='day'){
let d = new Date().toLocaleDateString().replace(/\//g,'-');
let d = DayJs().format("YYYY/MM/DD HH:mm:ss");
if(this.defaultValue){
d = this.defaultValue;
}
@@ -343,10 +351,9 @@
},
methods: {
watchRangeTime(){
let d = new Date().toLocaleDateString().replace(/\//g,'-');
let d = DayJs().format("YYYY/MM/DD HH:mm:ss")
if(this.defaultValue){
d = this.defaultValue;
d = DayJs(this.defaultValue).format("YYYY/MM/DD HH:mm:ss");
}
this.cal = new this.$tm.calendar({value:d,start:this.timeStart,end:this.timeEnd})
if(this.txt){
@@ -366,8 +373,8 @@
if(this.bingStart&&this.bingEnd){
this.fanxuanxuanwuBydate(
new Date(this.bingStart.replace(/-/g,'/')),
new Date(this.bingEnd.replace(/-/g,'/')),
DayJs(this.bingStart),
DayJs(this.bingEnd),
)
}
}
@@ -609,13 +616,14 @@
// 通过外围 时间默认的选中
fanxuanxuanwuBydate(start,end){
if(!start||!end) return;
// .format("YYYY/MM/DD HH:mm:ss")
this.$nextTick(function(){
if(start.getTime()>end.getTime()) return;
if(start.valueOf()>end.valueOf()) return;
// 获取开始月份的数据。
let sobj = new this.$tm.calendar({value:start.toLocaleDateString().replace(/\//g,'-')});
let sobj = new this.$tm.calendar({value:start.format("YYYY/MM/DD HH:mm:ss")});
// 获取结束月份的数据。
let eobj = new this.$tm.calendar({value:end.toLocaleDateString().replace(/\//g,'-')});
let eobj = new this.$tm.calendar({value:end.format("YYYY/MM/DD HH:mm:ss")});
function findItemToindex_only(obj,type){
let item=null;
for(let i=0;i<obj.length;i++){
@@ -657,7 +665,7 @@
this.start = start_obj
this.end = end_obj
// 如果结束和开始相等。
if(start.getTime()==end.getTime()){
if(start.valueOf()==end.valueOf()){
this.start = {...this.start,start:true,end:true}
this.end = {...this.end,start:true,end:true}
}
@@ -48,12 +48,21 @@
:key="index"><text class="text-size-s py-16">{{item}}</text>
</tm-col>
<!-- #endif -->
<tm-col :round="(item.start||item.end||item.checked)?4:0" @click="day_danxuanclick(item,index)"
:color="item.beginEnd?(item.checked===true||item.start||item.end?color_tmeme+(black_tmeme?' bk ':''):(item.guocheng?color_tmeme+' text opacity-7 '+(black_tmeme?'bk':''):'')):''"
<tm-col
@click="day_danxuanclick(item,index)"
color=""
:custom-class="isSelectedDateClass(item)"
align="middle" width="14.28%" v-for="(item,index) in nowData"
:key="index">
<view class="tm-calendarView-col flex-center flex-col" :class="[black&&!item.beginEnd?' opacity-2 ':'']">
justify="center" width="14.28%"
v-for="(item,index) in nowData"
:key="index"
>
<view class="tm-calendar-col flex-center flex-col "
:style="[mode=='rang'?{width:'100%',height:'90rpx'}:{width:'90rpx',height:'90rpx',overflow: 'hidden','border-radius': '50% !important'}]"
:class="[
item.start?'round-l-24':'',
item.end?'round-r-24':'',
item.beginEnd?(item.checked===true||item.start||item.end?color_tmeme+(black_tmeme?' bk ':''):(item.guocheng?color_tmeme+' text opacity-7 '+(black_tmeme?'bk':''):'')):'',
black_tmeme&&!item.beginEnd?' opacity-2 ':'']">
<text class="text-size-n"
:class="[
!item.nowMonth&&!item.guocheng&&!item.checked&&!item.start&&!item.end?(black_tmeme?'text-grey-darken-3':'text-grey-lighten-1'):'',
@@ -65,10 +74,10 @@
<view class="text-size-xs text_bl"
>
<block v-if="item.start">
</block>
<block v-if="item.end">
结束
-止
</block>
<block v-if="!item.start&&!item.end">
@@ -53,7 +53,7 @@
return ps;
},
isfinish:function(){
if(this.now == this.time) return true;
if(this.now == this.time||this.now==0) return true;
return false;
}
},
@@ -302,19 +302,19 @@ export default {
.tm-dialog {
width: 100%;
z-index: 600;
background-color: rgba(0, 0, 0, 0.35);
background-color: rgba(0, 0, 0, 0.3);
left: 0;
top: 0;
backdrop-filter: blur(10px);
transition: all 0.35s;
backdrop-filter: blur(3px);
transition: all 0.24s;
.success {
animation: success 0.35s linear;
animation: success 0.24s ease;
// transform: scale(1);
}
.clickover {
animation: clickover 0.35s linear;
animation: clickover 0.24s ease;
}
}
@keyframes clickover {
@@ -253,10 +253,10 @@
this.$emit('change',elList);
},
inits() {
this.grid = this.list.length;
if(this.grid==0) return;
this.$nextTick(async function() {
let p = await this.$Querey(".tm-dragGrid", this).catch(e => {})
this.grid = this.list.length;
this.listData = [];
//
this.w = uni.upx2px(this.width) || p[0].width || 300;
@@ -140,7 +140,6 @@
jishunTopData() {
this.$nextTick(async function() {
this.listData = [];
let p = await this.$Querey(".tm-dragList", this).catch(e => {})
this.w = uni.upx2px(this.width) || p[0].width;
this.h = uni.upx2px(this.height)
@@ -20,6 +20,7 @@
@touchcancel="$emit('touchcancel', $event)"
:open-type="openType"
@click="click"
:padding="[0,0]"
:showValue="showText" vertical :label="label" :fontSize="fontSize"
:iconSize="iconSize" :theme="color_tmeme" round="rouned" :font-color="fontColor" :bgcolor="bgcolor"
:size="size" :width="width" :height="width" :icon="icon" fab>
@@ -6,7 +6,7 @@
<view v-if="title&&title!='true'" class="px-32 py-16 text-weight-b " :class="[`text-size-${fontSize}`,titleTheme, black_tmeme ? 'bk' : '']">
<slot name="title" :title="title">{{ title }}</slot>
</view>
<view v-show="chuliWsok==true">
<view >
<slot></slot>
</view>
</view>
@@ -77,6 +77,20 @@ export default {
return this.$tm.vx.state().tmVuetify.black;
}
},
provide(){
let t = this;
return {
GroupListStyles:()=>{
return {
margin: [0,0],
padding: [32,24],
shadow: 0,
round: 0,
borderBottom: true
}
}
}
},
data() {
return {
targ:'tm-grouplist',
@@ -99,47 +113,7 @@ export default {
},
methods: {
inits(){
let t = this;
this.$nextTick(function () {
let ch = this.$children;
// #ifdef H5
ch = [];
let cld = this.$children[0].$children
while(cld){
if(cld[0].$children.length>0){
let ods = cld[0].$children;
ch =ods[ods.length-1].$children;
break;
}else{
cld = cld[0]?.$children
}
}
// #endif
ch.forEach((item,index)=>{
if(item.$options.name==="tm-listitem"){
item.setConfig({
margin: [0,0],
padding: [32,24],
shadow: 0,
round: 0,
borderBottom: index===ch.length-1?false:t.borderBottom
})
}
})
// #ifndef H5
setTimeout(function() {
t.chuliWsok = true;
}, 30);
// #endif
})
},
// tm-listitemgroup
change(vue_uid) {
+160 -157
View File
@@ -1,187 +1,190 @@
<template>
<!-- 图标 -->
<view @click="onclick" v-if="name" class="tm-icons vertical-align-top" style="display: inline-block;font-size: 0;" >
<view class="flex-center " :style="{
width: sizes,
height: sizes,
lineHeight:sizes,
}">
<view @click="onclick" v-if="name" class="tm-icons " >
<view
class="tm-icons-item "
:style="{
width: sizes,
height: sizes,
lineHeight: sizes
}"
>
<block v-if="vtype == false">
<image :src="name" :style="{
<image
:src="name"
:style="{
width: sizes,
height: sizes
}" mode="scaleToFill"></image>
}"
mode="scaleToFill"
></image>
</block>
<block v-if="vtype == true">
<!-- #ifdef APP-NVUE -->
<text :style="{
<text
:style="{
fontSize: sizes,
fontFamily:'iconfontTM'
}" class="icons "
:class="[ black_tmeme ? colors+'-bk':colors, dense ? '' : 'pa-10', black ? 'opacity-6' : '']">{{iconName}}</text>
fontFamily: 'iconfontTM'
}"
class="icons "
:class="[black_tmeme ? colors + '-bk' : colors, dense ? '' : 'pa-10', black ? 'opacity-6' : '']"
>
{{ iconName }}
</text>
<!-- #endif -->
<!-- #ifdef MP -->
<text :style="{
<!-- #ifndef APP-NVUE -->
<text
:style="{
fontSize: sizes,
}" class="icons mt--4"
:class="[prefx_computed, black_tmeme ? 'bk' : '', iconName, colorTheme ? colors : '', dense ? '' : 'pa-10', black ? 'opacity-6' : '']"></text>
}"
class="icons "
:class="[prefx_computed, black_tmeme ? 'bk' : '', iconName, colorTheme ? colors : '', dense ? '' : 'pa-10', black ? 'opacity-6' : '']"
></text>
<!-- #endif -->
<!-- #ifndef MP -->
<text :style="{
fontSize: sizes,
}" class="icons "
:class="[prefx_computed, black_tmeme ? 'bk' : '', iconName, colorTheme ? colors : '', dense ? '' : 'pa-10', black ? 'opacity-6' : '']"></text>
<!-- #endif -->
</block>
</view>
</view>
</template>
<script>
/**
* 图标组件
* @property {Boolean} dense = [true|false] 默认false,是否去除边距
* @property {String} prefx = [iconfont] 默认iconfont,默认图标的前缀对自定义图标时有好处
* @property {String} name = [] 默认'',图标名称注意不带前缀
* @property {String | Number} size = [] 默认28, 图标大小单位是upx
* @property {String} color = [primary] 默认primary, 图标主题颜色名
* @property {Function} click 图标点击事件
* @example <tm-icons name='icon-clear'></tm-icons>
*/
export default {
props: {
dense: {
//
type: Boolean,
default: false
},
black: {
type: Boolean,
default: null
},
prefx: {
type: String, //
default: 'iconfont'
},
name: {
type: String, //
default: ''
},
size: {
type: String | Number, //
default: 28
},
color: {
type: String, //
default: 'primary'
},
//svg
iconType: {
type: String,
default: '' //img|icon
},
//
fllowTheme: {
type: Boolean | String,
default: false
/**
* 图标组件
* @property {Boolean} dense = [true|false] 默认false,是否去除边距
* @property {String} prefx = [iconfont] 默认iconfont,默认图标的前缀对自定义图标时有好处
* @property {String} name = [] 默认'',图标名称注意不带前缀
* @property {String | Number} size = [] 默认28, 图标大小单位是upx
* @property {String} color = [primary] 默认primary, 图标主题颜色名
* @property {Function} click 图标点击事件
* @example <tm-icons name='icon-clear'></tm-icons>
*/
export default {
props: {
dense: {
//
type: Boolean,
default: false
},
black: {
type: Boolean,
default: null
},
prefx: {
type: String, //
default: 'iconfont'
},
name: {
type: String, //
default: ''
},
size: {
type: String | Number, //
default: 28
},
color: {
type: String|null, //
default: 'primary'
},
//svg
iconType: {
type: String,
default: '' //img|icon
},
//
fllowTheme: {
type: Boolean | String,
default: false
}
},
computed: {
iconName: function() {
// #ifdef APP-NVUE || APP-PLUS-NVUE
let fontList = require('@/tm-vuetify/scss/iconfonts/iconfont.json');
let name = this.name.replace('icon-', '');
// fontList.glyphs
let itemIcon = fontList.glyphs.find((item, index) => {
return item.font_class == name;
});
return eval('"\\u' + itemIcon.unicode + '"');
// #endif
return this.name;
},
prefx_computed() {
let prefix = this.name.split('-')[0];
if (prefix == 'icon') return 'iconfont';
if (prefix == 'mdi') return 'mdi';
return prefix;
},
black_tmeme: function() {
return this.$tm.vx.state().tmVuetify.black;
},
vtype: function() {
if (this.name[0] == '.' || this.name[0] == '/' || this.name.substring(0, 4) == 'http' || this.name.substring(0, 5) == 'https' || this.name.substring(0, 3) == 'ftp') {
return false;
}
return true;
},
sizes: function() {
if (typeof this.size === 'string') {
if (/[rpx|upx|rem|em|vx|vh|px]$/.test(this.size)) {
return this.size;
}
return uni.upx2px(parseInt(this.size)) + 'px';
}
if (typeof this.size === 'number' && !isNaN(this.size)) {
return uni.upx2px(this.size) + 'px';
}
},
computed: {
iconName: function() {
// #ifdef APP-NVUE || APP-PLUS-NVUE
let fontList = require('@/tm-vuetify/scss/iconfonts/iconfont.json');
let name = this.name.replace('icon-', '');
// fontList.glyphs
let itemIcon = fontList.glyphs.find((item, index) => {
return item.font_class == name;
})
return eval('"\\u' + itemIcon.unicode + '"');
// #endif
return this.name;
},
prefx_computed(){
let prefix = this.name.split('-')[0];
if(prefix=='icon') return 'iconfont';
if(prefix=='mdi') return 'mdi';
return prefix;
},
black_tmeme: function() {
return this.$tm.vx.state().tmVuetify.black;
},
vtype: function() {
if (this.name[0] == "." ||
this.name[0] == "/" ||
this.name.substring(0, 4) == 'http' ||
this.name.substring(0, 5) == 'https' ||
this.name.substring(0, 3) == 'ftp'
) {
return false;
}
return true;
},
sizes: function() {
if (typeof this.size === 'string') {
if (/[rpx|upx|rem|em|vx|vh|px]$/.test(this.size)) {
return this.size;
}
return uni.upx2px(parseInt(this.size)) + 'px';
}
if (typeof this.size === 'number' && !isNaN(this.size)) {
return uni.upx2px(this.size) + 'px';
}
},
color_tmeme: function() {
if (this.$tm.vx.state().tmVuetify.color !== null && this.$tm.vx.state().tmVuetify.color && this
.fllowTheme) {
return this.$tm.vx.state().tmVuetify.color;
}
return this.color;
},
colors: {
get: function() {
if (!this.color_tmeme) return 'text-primary';
if (!this.$TestColor(this.color_tmeme)) {
return this.color_tmeme;
}
return 'text-' + this.color_tmeme;
},
set: function() {
if (!this.$TestColor(this.color_tmeme)) {
this.colorTheme = false;
return this.color_tmeme;
}
this.colorTheme = true;
}
color_tmeme: function() {
if (this.$tm.vx.state().tmVuetify.color !== null && this.$tm.vx.state().tmVuetify.color && this.fllowTheme) {
return this.$tm.vx.state().tmVuetify.color;
}
return this.color;
},
data() {
return {
colorTheme: true
};
},
methods: {
onclick(e) {
this.$emit('click', e);
colors: {
get: function() {
if (!this.color_tmeme) return 'text-primary';
if (!this.$TestColor(this.color_tmeme)) {
return this.color_tmeme;
}
return 'text-' + this.color_tmeme;
},
set: function() {
if (!this.$TestColor(this.color_tmeme)) {
this.colorTheme = false;
return this.color_tmeme;
}
this.colorTheme = true;
}
}
};
},
data() {
return {
colorTheme: true
};
},
methods: {
onclick(e) {
this.$emit('click', e);
}
}
};
</script>
<style lang="scss" scoped>
.tm-icons {
vertical-align: middle;
.icons {
&.black {
color: #fff;
}
.tm-icons {
display: inline-block;
.tm-icons-item{
display: flex;
justify-content: center;
align-items: center;
}
.icons {
&.black {
color: #fff;
}
}
}
</style>
+5 -2
View File
@@ -25,7 +25,7 @@
<view v-if="title" class="d-inline-block "
:style="{fontSize:title_size}"
:class="[titleClass,black_tmeme?'bk text-grey-lighten-3':'']">
<text v-if="required" class="text-red">*</text>
<text v-if="_required" class="text-red">*</text>
<slot name="default" :title="title">
{{title}}
</slot>
@@ -368,6 +368,9 @@
};
},
computed: {
_required(){
return this.required
},
height_rpx:function(){
return this.height;
},
@@ -430,7 +433,7 @@
set:function(val){
this.$emit('input', val)
this.$emit('update:value', val)
if (this.required) {
if (this._required) {
this.$nextTick(function(){
this.verifyInput();
})
@@ -1,6 +1,6 @@
<!-- 列表项目类似单元格 -->
<template>
<view class="tm-listitem ">
<view class="tm-listitem " :hover-class="url?'opacity-6':''">
<view
@click="click"
@@ -280,9 +280,15 @@ export default {
return this.$tm.vx.state().tmVuetify.color;
}
return this.leftIconColor;
},
groupPublickStyle:function(){
if(!this.GroupListStyles) return null
return this.GroupListStyles()
}
},
inject:{
GroupListStyles:{default:null}
},
data() {
return {
pz_theme: {
@@ -296,13 +302,25 @@ export default {
};
},
created() {
this.pz_themeCus = {
margin: this.margin,
padding: this.padding,
shadow: this.shadow,
round: this.round,
borderBottom: this.borderBottom
};
if(this.groupPublickStyle){
this.pz_themeCus = {
margin: this.groupPublickStyle.margin,
padding: this.groupPublickStyle.padding,
shadow: this.groupPublickStyle.shadow,
round: this.groupPublickStyle.round,
borderBottom: this.groupPublickStyle.borderBottom||this.borderBottom
};
}else{
this.pz_themeCus = {
margin: this.margin,
padding: this.padding,
shadow: this.shadow,
round: this.round,
borderBottom: this.borderBottom
};
}
},
mounted() {
@@ -22,7 +22,6 @@
<text class="iconfont icon-angle-left" :class="[`text-${fontColorTheme}`]" :style="{ fontSize: '28rpx' }"></text>
</navigator>
</block>
<slot name="left" :data="{ style: widths, isTransparent: isTransparent, title: title }"></slot>
</view>
<view class="center flex-center text-size-g text-overflow text-align-center" :class="[`text-${fontColorTheme}`]">
@@ -36,6 +35,7 @@
</view>
</view>
</view>
</template>
<script>
@@ -61,6 +61,9 @@
components: {
tmIcons
},
options:{
multipleSlots: true
},
name: 'tm-menubars',
props: {
//
+12 -10
View File
@@ -109,18 +109,12 @@ import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
mounted() {
this.$nextTick(function(){
this.downOpen = this.open;
let t = this;
t.$Querey('.tm-more-content',t).then(syninfo=>{
if (syninfo[0].height - 40 <= t.hs) {
t.show = false;
}
}).catch(e => {});
this.getRect()
})
},
updated() {
this.getRect()
},
computed: {
hs: function() {
return uni.upx2px(this.maxHeight) || 100;
@@ -131,6 +125,14 @@ import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
}
},
methods: {
getRect(){
let t = this;
t.$Querey('.tm-more-content',t,0).then(syninfo=>{
if (syninfo[0].height - 40 <= t.hs) {
t.show = false;
}
}).catch(e => {});
},
openMore() {
this.$emit('click');
if (this.disabled) return;
+7 -7
View File
@@ -232,7 +232,7 @@
this.aniData = '';
let aniData = uni.createAnimation({
duration:240,
timingFunction: 'linear',
timingFunction: 'ease',
})
this.aniData = aniData;
if(pos=='bottom'){
@@ -327,15 +327,15 @@
pointer-events: none;
}
&.blur {
backdrop-filter: blur(10px);
backdrop-filter: blur(3px);
}
&.on {
animation: opta 1s linear;
animation: opta 1s ease;
}
&.off {
animation: opta_off 0.35s linear;
animation: opta_off 0.24s ease;
}
.tm-poup-wk {
position: absolute;
@@ -393,7 +393,7 @@
@keyframes opta {
from {
opacity: 0.6;
opacity: 0.3;
}
to {
@@ -495,7 +495,7 @@
@keyframes Centerleft {
from {
transform: scale(0.65);
opacity: 0.65;
opacity: 0.3;
}
to {
@@ -512,7 +512,7 @@
to {
transform: scale(0.65);
opacity: 0.65;
opacity: 0.3;
}
}
@@ -166,19 +166,25 @@ export default {
destroyed() {
clearInterval(this.tmiddd)
},
async mounted() {
this.$nextTick(async function(){
let p = await this.$Querey(".tm-propress",this).catch(e=>{})
this.width_upx = p[0].width+'px';
if(this.loading){
await this.startAni();
}
})
mounted() {
this.getRect()
},
updated() {
this.getRect()
},
methods: {
getRect(){
let t =this
this.$Querey(".tm-propress",this,0).then((p)=>{
if(!p) return;
if(!p[0].width) return;
t.width_upx = p[0].width+'px';
if(t.loading){
t.startAni();
}
}).catch(e=>{})
},
async startAni(){
// clearInterval(this.tmiddd)
let t = this;
+27 -26
View File
@@ -1,10 +1,7 @@
<template>
<view @click="$emit('click',$event)" :gutter="gutter" class="tm--row" :class="[preatClass]" >
<view class="tm--row--body" :style="style" :class="[customClass]">
<view v-show="chuliWsok==true">
<slot></slot>
</view>
<slot></slot>
</view>
</view>
</template>
@@ -64,30 +61,34 @@
width_px:0,
children_num:0,
style:'',
chuliWsok:false,//
};
},
async mounted() {
let t = this;
await this.$Querey('.tm--row',this).then(preantw=>{
t.width_px = preantw[0].width;
// #ifndef H5
t.children_num = t.$children.length;
// #endif
// #ifdef H5
t.children_num = t.$children[0].$children[0].$children[0].$children.length;
// #endif
t.style = uni.$tm.objToString({
'justify-content':t.justify,
'align-items':t.align,
'width':t.width,
'height':!t.height?'default':(uni.upx2px(t.height)+'px')
},';');
t.chuliWsok = true;
}).catch(e=>{})
updated() {
this.getContinaRect()
},
mounted() {
this.getContinaRect()
},
methods:{
getContinaRect(){
let t = this;
this.$Querey('.tm--row',this).then(preantw=>{
t.width_px = preantw[0].width;
// #ifndef H5
t.children_num = t.$children.length;
// #endif
// #ifdef H5
t.children_num = t.$children[0].$children[0].$children[0].$children.length;
// #endif
t.style = uni.$tm.objToString({
'justify-content':t.justify,
'align-items':t.align,
'width':t.width,
'height':!t.height?'default':(uni.upx2px(t.height)+'px')
},';');
}).catch(e=>{})
}
}
}
</script>
@@ -191,10 +191,10 @@
},
mounted() {
let t = this;
uni.$tm.sleep(30).then(()=>{
t.setInits();
})
t.setInits();
},
updated() {
this.setInits();
},
methods: {
setInits() {
+2 -20
View File
@@ -25,7 +25,7 @@
]"
>
<view class="fulled" v-if="showSheet">
<view class="fulled" >
<slot name="default"></slot>
</view>
</view>
@@ -151,25 +151,7 @@ export default {
return this.$tm.vx.state().tmVuetify.black;
}
},
data() {
return {
showSheet:true,
};
},
created() {
// #ifdef APP-VUE || APP-PLUS || MP
this.showSheet = false;
// #endif
},
mounted() {
let t= this;
// #ifdef APP-VUE || APP-PLUS || MP
setTimeout(function() {
t.showSheet = true;
}, 30);
// #endif
}
};
</script>
<style lang="scss" scoped>
@@ -12,7 +12,6 @@
@touchstart="touchstart"
@touchmove.stop.prevent="touchsmove"
@touchend="touchsend"
@mousedown="touchstart"
@mousemove.stop.prevent="touchsmove"
@mouseup="touchsend"
+12 -1
View File
@@ -244,6 +244,17 @@
this.barLeft = rdl >= this.sliderWidth || rdl < 0 ? this.sliderWidth : rdl;
});
},
async updated() {
this.$nextTick(async function() {
await this.getwidth();
if (Math.abs(this.value) > Math.abs(this.max)) {
this.isError = true;
return;
}
let rdl = this.sliderWidth * (Math.abs(this.value) / Math.abs(this.max));
this.barLeft = rdl >= this.sliderWidth || rdl < 0 ? this.sliderWidth : rdl;
});
},
methods: {
barStart(e) {
@@ -306,7 +317,7 @@
},
async getwidth() {
let res = await this.$Querey('.tm-slider-id', this).catch(e=>{});
let res = await this.$Querey('.tm-slider-id', this,0).catch(e=>{});
res[0].width = res[0].width||uni.upx2px(this.width);
res[0].height = res[0].height||uni.upx2px(this.height);
if (this.showLeft === false && this.showRight === false) {
@@ -297,6 +297,20 @@
this.barLeft_1 = rdl_1 >= this.sliderWidth || rdl_1 < 0 ? this.sliderWidth : rdl_1;
});
},
updated() {
this.$nextTick(async function() {
await this.getwidth();
if (Math.abs(this.value[1]) > Math.abs(this.max)) {
this.isError = true;
return;
}
let rdl = this.sliderWidth * (Math.abs(this.value[0]) / Math.abs(this.max));
this.barLeft = rdl >= this.sliderWidth || rdl < 0 ? this.sliderWidth : rdl;
let rdl_1 = this.sliderWidth * (Math.abs(this.value[1]) / Math.abs(this.max));
this.barLeft_1 = rdl_1 >= this.sliderWidth || rdl_1 < 0 ? this.sliderWidth : rdl_1;
});
},
methods: {
barStart(e) {
if (this.disabled || this.isError) return;
@@ -407,7 +421,7 @@
},
async getwidth() {
let res = await this.$Querey('.tm-slider-id', this).catch(e=>{});
let res = await this.$Querey('.tm-slider-id', this,0).catch(e=>{});
res[0].width = res[0].width||uni.upx2px(this.width);
res[0].height = res[0].height||uni.upx2px(this.height);
+358 -399
View File
@@ -1,83 +1,43 @@
<template>
<view class="tm-swiper " :class="[inline ? 'd-inline-block' : '']">
<swiper
:previous-margin="`${ani3d}rpx`"
:next-margin="`${ani3d}rpx`"
:style="{
<swiper :previous-margin="`${ani3d}rpx`" :next-margin="`${ani3d}rpx`" :style="{
width: w_s + 'rpx',
height: h_s + 'rpx'
}"
:vertical="vertical"
:autoplay="autoplay && !isPlayVedio"
:circular="circular"
:interval="interval"
:duration="duration"
:indicator-active-color="color_tmeme"
:current="nowIndex"
@change="change"
>
}" :vertical="vertical" :autoplay="autoplay&&!isPlayVedio" :circular="circular" :interval="interval" :duration="duration"
:indicator-active-color="color_tmeme" :current="nowIndex" @change="change" :class="[round_num > 0 ? ` round-${round_num} overflow` : '']">
<block v-for="(item, index) in dataList" :key="index">
<swiper-item
:style="{
<swiper-item :style="{
width: w_s + 'rpx',
height: h_s + 'rpx'
}"
class="shadow-24"
:class="[round_num > 0 ? ` round-${round_num} overflow` : '']"
>
}" class="shadow-24" :class="[round_num > 0 ? ` round-${round_num} overflow` : '']">
<view
:class="[
nowIndex !== index && ani3d > 0 ? 'a3d' : '',
round_num > 0 ? ` round-${round_num} overflow` : '',
nowIndex < index && ani3d > 0 ? 'a3dL' : '',
nowIndex > index && ani3d > 0 ? 'a3dR' : ''
]"
class="tm-swiper-ik fulled fulled-height"
:key="index"
>
:class="[nowIndex !== index&&ani3d>0?'a3d':'',round_num > 0 ? ` round-${round_num} overflow` : '',nowIndex<index&&ani3d>0?'a3dL':'',nowIndex>index&&ani3d>0?'a3dR':'']"
class="tm-swiper-ik fulled fulled-height" :key="index">
<view class="actvieMarginDh">
<view class="actvieMarginDh-item" :class="[round_num > 0 ? ` round-${round_num} overflow` : '']">
<image
v-if="item.dtype != 'video'"
@click="itemClick(item)"
:style="{
<view class="actvieMarginDh-item"
:class="[round_num > 0 ? ` round-${round_num} overflow` : '']">
<image v-if="item.dtype!='video'" @click="itemClick(item)" :style="{
height: h_s + 'rpx',
width: w_s - margin_px + 'rpx'
}"
@load="imgload($event, index)"
:src="item.url"
:class="[round_num > 0 ? ` round-${round_num} overflow` : '']"
></image>
<video
@play="isPlayVedio = true"
@pause="isPlayVedio = false"
@ended="isPlayVedio = false"
:autoplay="nowIndex == index ? true : false"
:style="{
width: (w_s - margin_px) + 'rpx'
}" @load="imgload($event, index)" :src="item.url"
:class="[round_num > 0 ? ` round-${round_num} overflow` : '']"></image>
<video @play="isPlayVedio = true" @pause="isPlayVedio=false" @ended="isPlayVedio=false" :autoplay="nowIndex == index?true:false" :style="{
height: h_s + 'rpx',
width: w_s - margin_px + 'rpx'
}"
v-if="item.dtype == 'video'"
:src="item.url"
></video>
width: (w_s - margin_px) + 'rpx'
}" v-if="item.dtype=='video'" :src="item.url"></video>
<view
v-if="item.title"
:style="{
width: w_s - margin_px + 'rpx'
}"
class="flex-start relative "
>
<view v-if="item.title" :style="{
width: (w_s - margin_px) + 'rpx'
}" class="flex-start relative ">
<view
class="text-size-s px-16 absolute b-0 l-0 zIndex-14 flex-start text-overflow text-white"
:style="{
height: '66rpx',
width: w_s - margin_px - 32 + 'rpx',
lineHeight: '66rpx',
background: 'rgba(0,0,0,0.4)'
}"
>
{{ item.title }}
height: '66rpx',
width: (w_s - margin_px-32) + 'rpx',
lineHeight:'66rpx',
background:'rgba(0,0,0,0.4)'
}">
{{item.title}}
</view>
</view>
</view>
@@ -86,24 +46,19 @@
</swiper-item>
</block>
</swiper>
<view class="tm-swiper-dot" v-if="indicatorDots && nowshowDot" :class="[dotDirection]">
<view class="tm-swiper-dot" v-if="indicatorDots&&nowshowDot" :class="[dotDirection]">
<block v-if="dotModel != 'round'">
<view
@click="nowIndex = index"
v-for="(item, index) in dataList"
:key="index"
<view @click="nowIndex = index" v-for="(item, index) in dataList" :key="index"
class="tm-swiper-dot-item flex-center mx-8"
:class="[nowIndex == index ? color_tmeme : 'unactive', dotModel]"
>
:class="[nowIndex == index ? color_tmeme : 'unactive', dotModel]">
<text v-if="dotModel === 'number'" class="text-size-xs">{{ index + 1 }}</text>
</view>
</block>
<block v-if="dotModel == 'round'">
<view
:class="[dotModel]"
<view :class="[dotModel]"
class="text-size-xs balck text-white round-24 px-24 tm-swiper-dot-item flex-center flex-shrink"
:style="{ height: '40rpx', minWidth: '60rpx' }"
>
:style="{ height: '40rpx', minWidth: '60rpx' }">
{{ nowIndex + 1 + '/' + dataList.length }}
</view>
</block>
@@ -112,353 +67,357 @@
</template>
<script>
/**
* 图片轮播
* @description 很参数与原官方相同
* @property {Number} width = [] 默认0轮播宽度0为自己计算
* @property {Number} height = [] 默认0轮播高度0为自己计算
* @property {Array} list = [] 默认[]图片列表可以是string数组或者object数组
* @property {String} rang-key = [] 默认[]图片列表object数组时需要提供图片地址 的键值
* @property {Boolean} inline = [] 默认false是否内联方便一排放多个轮播组件
* @property {Boolean} previmage = [] 默认true,是否预览图片点击图片时放大预览
* @property {Boolean} vertical = [] 默认false,是否竖向滚动
* @property {Boolean} circular = [] 默认false,是否采用衔接滑动即播放到末尾后重新回到开头
* @property {Boolean} autoplay = [] 默认false,是否自动切换
* @property {Number} interval = [] 默认3000,自动切换时间间隔
* @property {Number} duration = [] 默认500,滑动动画时长
* @property {Number} ani3d = [] 默认0,开启3d缩放动画
* @property {Boolean} indicator-dots = [] 默认true,是否显示指示
* @property {String} color = [] 默认primary,指示点的主题色
* @property {Number} current = [] 默认0,如果需要双向绑定请使用current.sync.
* @property {String} dot-model = [dot|number|rect|round] 默认dot,指示点的类型
* @property {String} dot-direction = [left|center|right] 默认center,指示点的位置
* @property {Number} round = [] 默认0,圆角,单位rpx
* @property {Number} margin = [] 默认0,单位rpx
* @property {Function} change 轮播切换时触发参数返回当前播放位置
* @property {Function} click 轮播项目被点击时触发发,返回项目数据
* @example <tm-swiper :list="['https://picsum.photos/300?jv=3','https://picsum.photos/300?jv=3']" ></tm-swiper>
*/
export default {
name: 'tm-swiper',
props: {
width: {
type: Number | String,
default: 0
},
//
height: {
type: Number | String,
default: 0
},
list: {
type: Array,
default: () => {
return [];
}
},
rangKey: {
type: String,
default: 'src'
},
//
inline: {
type: Boolean,
default: false
},
//
previmage: {
type: Boolean | String,
default: true
},
vertical: false,
circular: false,
autoplay: false,
interval: {
type: Number,
default: 3000
},
duration: {
type: Number,
default: 500
},
//
indicatorDots: false,
//
color: {
type: String,
default: 'primary'
},
//
current: {
type: Number,
default: 0
},
ani3d: {
type: Number,
default: 0
},
//
dotModel: {
type: String,
default: 'dot' //dot|number|rect
},
//
dotDirection: {
type: String,
default: 'center' //left|center|right
},
round: {
type: Boolean | Number,
default: 0
},
margin: {
type: Number,
default: 0
},
//
fllowTheme: {
type: Boolean | String,
default: true
}
},
watch: {
current: function(newval) {
// if (newval >= this.dataList.length) {
// this.nowIndex = 0;
// return;
// }
// this.nowIndex = newval;
},
list: {
deep: true,
handler() {
this.chulidata();
}
}
},
computed: {
color_tmeme: function() {
if (this.$tm.vx.state().tmVuetify.color !== null && this.$tm.vx.state().tmVuetify.color && this.fllowTheme) {
return this.$tm.vx.state().tmVuetify.color;
}
return this.color;
},
round_num: function() {
if (typeof this.round === 'boolean') return 0;
return this.round;
},
nowIndex: {
get: function() {
return this.dotIndex;
/**
* 图片轮播
* @description 很参数与原官方相同
* @property {Number} width = [] 默认0轮播宽度0为自己计算
* @property {Number} height = [] 默认0轮播高度0为自己计算
* @property {Array} list = [] 默认[]图片列表可以是string数组或者object数组
* @property {String} rang-key = [] 默认[]图片列表object数组时需要提供图片地址 的键值
* @property {Boolean} inline = [] 默认false是否内联方便一排放多个轮播组件
* @property {Boolean} previmage = [] 默认true,是否预览图片点击图片时放大预览
* @property {Boolean} vertical = [] 默认false,是否竖向滚动
* @property {Boolean} circular = [] 默认false,是否采用衔接滑动即播放到末尾后重新回到开头
* @property {Boolean} autoplay = [] 默认false,是否自动切换
* @property {Number} interval = [] 默认3000,自动切换时间间隔
* @property {Number} duration = [] 默认500,滑动动画时长
* @property {Number} ani3d = [] 默认0,开启3d缩放动画
* @property {Boolean} indicator-dots = [] 默认true,是否显示指示
* @property {String} color = [] 默认primary,指示点的主题色
* @property {Number} current = [] 默认0,如果需要双向绑定请使用current.sync.
* @property {String} dot-model = [dot|number|rect|round] 默认dot,指示点的类型
* @property {String} dot-direction = [left|center|right] 默认center,指示点的位置
* @property {Number} round = [] 默认0,圆角,单位rpx
* @property {Number} margin = [] 默认0,单位rpx
* @property {Function} change 轮播切换时触发参数返回当前播放位置
* @property {Function} click 轮播项目被点击时触发发,返回项目数据
* @example <tm-swiper :list="['https://picsum.photos/300?jv=3','https://picsum.photos/300?jv=3']" ></tm-swiper>
*/
export default {
name: 'tm-swiper',
props: {
width: {
type: Number | String,
default: 0
},
set: function(val) {
this.dotIndex = val;
this.$emit('update:current', val);
this.$emit('change', val);
}
},
nowshowDot: function() {
return this.showdot();
},
w_s: {
get: function() {
return this.w_w;
//
height: {
type: Number | String,
default: 0
},
set: function(val) {
this.w_w = val;
}
},
h_s: {
get: function() {
return this.h_h;
list: {
type: Array,
default: () => {
return [];
}
},
set: function(val) {
this.h_h = val;
rangKey: {
type: String,
default: 'src'
},
//
inline: {
type: Boolean,
default: false
},
//
previmage: {
type: Boolean | String,
default: true
},
vertical: false,
circular: false,
autoplay: false,
interval: {
type: Number,
default: 3000
},
duration: {
type: Number,
default: 500
},
//
indicatorDots: false,
//
color: {
type: String,
default: 'primary'
},
//
current: {
type: Number,
default: 0
},
ani3d: {
type: Number,
default: 0
},
//
dotModel: {
type: String,
default: 'dot' //dot|number|rect
},
//
dotDirection: {
type: String,
default: 'center' //left|center|right
},
round: {
type: Boolean | Number,
default: 0
},
margin: {
type: Number,
default: 0
},
//
fllowTheme: {
type: Boolean | String,
default: true
}
},
margin_px: function() {
return this.margin * 2;
}
},
data() {
return {
w_w: 0,
h_h: 0,
dataList: [],
dotIndex: 0,
isPlayVedio: false
};
},
async mounted() {
// this.nowIndex = this.current;
let w = this.width;
if (String(w).indexOf('px') > -1 && typeof w == 'string') {
let p = parseInt(h);
let sy = 750 / uni.getSystemInfoSync().windowWidth;
w = p * sy;
}
if (typeof w == 'string') {
w = parseInt(w);
}
this.w_w = w;
let h = this.height;
if (String(h).indexOf('px') > -1 && typeof h == 'string') {
let p = parseInt(h);
let sy = 750 / uni.getSystemInfoSync().windowWidth;
h = p * sy;
}
if (typeof h == 'string') {
h = parseInt(h);
}
this.h_h = h;
this.$nextTick(async function() {
await this.chulidata();
});
},
methods: {
showdot() {
if (typeof this.dataList[this.nowIndex] == 'string') return true;
if (typeof this.dataList[this.nowIndex] == 'object') {
if (this.dataList[this.nowIndex].dtype == 'video') return false;
watch: {
current: function(newval) {
if (newval >= this.dataList.length) {
this.nowIndex = 0;
return;
}
this.nowIndex = newval;
},
list: {
deep: true,
handler() {
this.chulidata();
}
}
return true;
},
async chulidata() {
let t = this;
let tb = await this.$Querey('.tm-swiper', this).catch(ev => {});
//console.log(uni.getSystemInfoSync());
let sy = 750 / uni.getSystemInfoSync().windowWidth;
let upxw = tb[0].width * sy;
let sw = this.w_s > 0 ? this.w_s : upxw > 0 ? upxw : 100;
this.w_s = sw;
let sh = this.h_s > 0 ? this.h_s : tb[0].height > 0 ? tb[0].height : 300;
this.h_s = sh;
let d = [];
this.list.forEach(item => {
if (typeof item === 'string') {
d.push({
width: sw,
height: sh,
url: item,
title: '',
dtype: '',
data: item
});
} else if (typeof item === 'object') {
d.push({
width: sw,
height: sh,
url: item[t.rangKey],
title: item['title'] || '',
dtype: item['type'] || '',
data: item
computed: {
color_tmeme: function() {
if (this.$tm.vx.state().tmVuetify.color !== null && this.$tm.vx.state().tmVuetify.color && this
.fllowTheme) {
return this.$tm.vx.state().tmVuetify.color;
}
return this.color;
},
round_num: function() {
if (typeof this.round === 'boolean') return 0;
return this.round;
},
nowIndex: {
get: function() {
return this.dotIndex;
},
set: function(val) {
this.dotIndex = val;
this.$emit('update:current', val);
this.$emit('change', val);
}
},
nowshowDot:function(){
return this.showdot();
},
w_s: {
get: function() {
return this.w_w;
},
set: function(val) {
this.w_w = val;
}
},
h_s: {
get: function() {
return this.h_h;
},
set: function(val) {
this.h_h = val;
}
},
margin_px: function() {
return this.margin * 2;
},
},
data() {
return {
w_w: 0,
h_h: 0,
dataList: [],
dotIndex: 0,
isPlayVedio:false,
};
},
async mounted() {
this.nowIndex = this.current;
let w = this.width;
if (String(w).indexOf('px') > -1 && typeof w == 'string') {
let p = parseInt(h);
let sy = 750 / uni.getSystemInfoSync().windowWidth;
w = p * sy;
}
if (typeof w == 'string') {
w = parseInt(w);
}
this.w_w = w;
let h = this.height;
if (String(h).indexOf('px') > -1 && typeof h == 'string') {
let p = parseInt(h);
let sy = 750 / uni.getSystemInfoSync().windowWidth;
h = p * sy;
}
if (typeof h == 'string') {
h = parseInt(h);
}
this.h_h = h;
this.$nextTick(async function() {
await this.chulidata();
});
},
methods: {
showdot(){
if(typeof this.dataList[this.nowIndex]=='string') return true;
if(typeof this.dataList[this.nowIndex]=='object'){
if(this.dataList[this.nowIndex].dtype=='video') return false;
}
return true;
},
async chulidata() {
let t = this;
let tb = await this.$Querey('.tm-swiper', this).catch(ev => {});
//console.log(uni.getSystemInfoSync());
let sy = 750 / uni.getSystemInfoSync().windowWidth;
let upxw = tb[0].width * sy;
let sw = this.w_s > 0 ? this.w_s : upxw > 0 ? upxw : 100;
this.w_s = sw;
let sh = this.h_s > 0 ? this.h_s : tb[0].height > 0 ? tb[0].height : 300;
this.h_s = sh;
let d = [];
this.list.forEach(item => {
if (typeof item === 'string') {
d.push({
width: sw,
height: sh,
url: item,
title: '',
dtype: '',
data: item
});
} else if (typeof item === 'object') {
d.push({
width: sw,
height: sh,
url: item[t.rangKey],
title: item['title'] || '',
dtype: item['type'] || '',
data: item
});
}
});
this.dataList = d;
},
imgload(e, index) {},
change(event) {
this.nowIndex = event.detail.current;
},
itemClick(e) {
if (this.previmage) {
uni.previewImage({
current: e.url,
urls: [e.url]
});
}
});
this.dataList = d;
},
imgload(e, index) {},
change(event) {
this.nowIndex = event.detail.current;
},
itemClick(e) {
if (this.previmage) {
uni.previewImage({
current: e.url,
urls: [e.url]
});
this.$emit('click', e);
}
this.$emit('click', e);
}
}
};
};
</script>
<style></style>
<style lang="scss" scoped>
.tm-swiper {
position: relative;
.tm-swiper {
position: relative;
.tm-swiper-ik {
transition: all 1s;
}
.a3d {
transform: scale(0.76);
}
.a3dL {
transform: scale(0.76) translateX(-24rpx);
}
.a3dR {
transform: scale(0.76) translateX(24rpx);
}
.actvieMarginDh {
display: flex;
justify-content: center;
.actvieMarginDh-item {
overflow: hidden;
line-height: 0;
}
}
.tm-swiper-dot {
.tm-swiper-dot-item {
width: 16upx;
height: 16upx;
border-radius: 50%;
&.unactive {
background-color: rgba(255, 255, 255, 0.6);
}
&.number {
width: 34upx;
height: 34upx;
}
&.rect {
width: 24upx;
height: 6upx;
border-radius: 0;
}
&.round {
background-color: rgba(0, 0, 0, 0.4);
}
.tm-swiper-ik {
transition: all 1s;
}
width: 100%;
position: absolute;
left: 0;
display: flex;
bottom: 20upx;
.a3d {
transform: scale(0.76);
}
&.center {
.a3dL {
transform: scale(0.76) translateX(-24rpx)
}
.a3dR {
transform: scale(0.76) translateX(24rpx)
}
.actvieMarginDh {
display: flex;
justify-content: center;
.actvieMarginDh-item {
overflow: hidden;
line-height: 0;
}
}
&.left {
display: flex;
justify-content: flex-start;
padding-left: 20upx;
width: calc(100% - 20upx);
}
.tm-swiper-dot {
.tm-swiper-dot-item {
width: 16upx;
height: 16upx;
border-radius: 50%;
&.right {
&.unactive {
background-color: rgba(255, 255, 255, 0.6);
}
&.number {
width: 34upx;
height: 34upx;
}
&.rect {
width: 24upx;
height: 6upx;
border-radius: 0;
}
&.round {
background-color: rgba(0, 0, 0, 0.4);
}
}
width: 100%;
position: absolute;
left: 0;
display: flex;
justify-content: flex-end;
padding-right: 20upx;
width: calc(100% - 20upx);
bottom: 20upx;
&.center {
display: flex;
justify-content: center;
}
&.left {
display: flex;
justify-content: flex-start;
padding-left: 20upx;
width: calc(100% - 20upx);
}
&.right {
display: flex;
justify-content: flex-end;
padding-right: 20upx;
width: calc(100% - 20upx);
}
}
}
}
</style>
@@ -13,7 +13,7 @@
* @property {Number} wait 延时
* @property {String} animation-name = [fadeUp|fadeDown|fadeLeft|fadeRight|zoomIn|zoomOut|fadeIn|fadeOut] 动画效果名称.
* @property {String|Boolean} auto = [true|false] 默认:true,是否自动播放动画.
* @example <tm-translate wait="500" animation-name="fadeUp"><tm-upload></tm-upload></tm-translate>
* @example <tm-translate wait="500" animation-name="fadeDown"><tm-upload></tm-upload></tm-translate>
*/
export default {
name:"tm-translate",
+60 -35
View File
@@ -1,5 +1,5 @@
<template>
<view class="tm-upload flex-start " id="tm-upload">
<view class="tm-upload flex-start relative" id="tm-upload">
<view v-for="(item,index) in list" :key="index" class="tm-upload-item " :class="[grid!=1?'ma-4':'']" :style="{
width:itemWidth+'px',
height:itemHeight+'px'
@@ -31,9 +31,18 @@
<!-- 上传的进度 -->
<view v-if="item.progress>0&&item.progress!=100&&!disabled" class="tm-upload-pro green"
:style="{width:item.progress+'%'}"></view>
<!-- 上传的排序 -->
<view v-if="showSort" class="absolute l-0 fulled flex-between" :class="[disabled?'b-0':'b-40']" :style="{height:'46rpx'}">
<view @click.stop="prevSort(item,index,'prev')" class="round-r-24 flex-center px-16 py-6" :class="[index==0?'opacity-0':'']" style="background-color: rgba(0, 0, 0, 0.3);">
<tm-icons name="icon-angle-left" size="24" :color="color_tmeme"></tm-icons>
</view>
<view @click.stop="prevSort(item,index,'next')" class="round-l-24 flex-center px-16 py-6" :class="[index==list.length-1?'opacity-0':'']" style="background-color: rgba(0, 0, 0, 0.3);">
<tm-icons name="icon-angle-right" size="24" :color="color_tmeme"></tm-icons>
</view>
</view>
</view>
<view @click="addfile" v-if="list.length<max&&!disabled&&showSheet" class="tm-upload-item ma-4 grey-lighten-4 " :class="[`round-${round}`]" :style="{
<view @click="addfile" v-if="list.length<max&&!disabled" class="tm-upload-item ma-4 grey-lighten-4 " :class="[`round-${round}`]" :style="{
width:itemWidth+'px',
height:itemHeight+'px'
}">
@@ -79,6 +88,10 @@
components:{tmIcons},
name: "tm-upload",
props: {
showSort:{
type:Boolean|String,
default:false
},
model:{
type:String,
default:'scaleToFill'
@@ -216,8 +229,7 @@
itemWidth: 0,
itemHeight: 0,
list: [],
//app使.
showSheet:true,
upObje:null,
};
},
@@ -228,13 +240,51 @@
},
async mounted() {
let t = this;
if (typeof t.filelist === 'object' && Array.isArray(t.filelist)) {
let plist = [...t.filelist];
plist.forEach((item, index) => {
let url = "";
if (typeof item === 'string') {
url = item;
} else if (typeof item === 'object') {
url = item[t.urlKey]
}
t.list.push({
url: url,
status: "上传成功",
progress: 100,
fileId: t.$tm.guid(),
statusCode: 3,
data: item,
})
})
this.$nextTick(async function(){
}
this.getRect()
this.$Querey('.tm-upload', this,30).then(o=>{
// #ifdef APP-VUE || APP-PLUS || MP
t.showSheet = true;
// #endif
},
updated() {
this.getRect()
},
methods: {
prevSort(item,index,type){
if((index==0&&type=='prev')||(index==this.list.length-1&&type=='next')){
return;
}
let nowindex = type=='prev'?index-1:index+1
let nowItem = this.list[index];
let newnowItem = this.list[nowindex];
let nowfilelist= [...this.list]
nowfilelist.splice(index,1,newnowItem)
nowfilelist.splice(nowindex,1,nowItem)
this.list = [...nowfilelist]
this.$emit('update:filelist', nowfilelist);
},
getRect(){
let t = this;
this.$Querey('.tm-upload', this,0).then(o=>{
if(!o[0].width&&t.maxWidth) return;
t.maxWidth = o[0].width||t.width;
let itemWidth = (t.maxWidth - (parseInt(t.grid) - 1) * uni.upx2px(12)) / parseInt(t.grid);
t.itemWidth = itemWidth;
@@ -242,33 +292,8 @@
if (t.imgHeight > 0) {
t.itemHeight = parseInt(uni.upx2px(t.imgHeight));
}
if (typeof t.filelist === 'object' && Array.isArray(t.filelist)) {
let plist = [...t.filelist];
plist.forEach((item, index) => {
let url = "";
if (typeof item === 'string') {
url = item;
} else if (typeof item === 'object') {
url = item[t.urlKey]
}
t.list.push({
url: url,
status: "上传成功",
progress: 100,
fileId: t.$tm.guid(),
statusCode: 3,
data: item,
})
})
}
})
})
},
methods: {
},
errorFile(item,index){
let id = item;
id['loaderror'] = true;
@@ -134,8 +134,10 @@
let day = nwod.getDate();
let week = nwod.getDay();
let new_weekDay = [nwod];
let timcha = Math.abs(week-7);
let zcha = Math.abs(7-timcha);
let weekCn=["周日","周一","周二","周三","周四","周五","周六"];
for(let i=1;i<zcha;i++){
let d = new Date(year,month,day-i);
@@ -157,9 +159,17 @@
}
weekDay.push(sc);
}
weekDay.sort(function(a, b){return a.week - b.week});
weekDay.push(weekDay[0])
weekDay.splice(0,1)
if(weekDay.length==8){
weekDay.splice(0,1)
weekDay.unshift(weekDay[weekDay.length-1])
weekDay.splice(weekDay.length-1,1)
}
let t= this;
this.timeList = [];
this.$nextTick(function(){
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "tm-vuetify",
"displayName": "tmUI-vuetify",
"version": "2.0.0",
"version": "2.0.1",
"description": "tm-vuetify是一个新势力由主题驱动的UI组件库,相比其它优势大,组件全,设计趋势紧跟未来。具有主题生成,主题实时切换,暗黑实时切换,lottie动画,图表等新颖功能,tmui TMUI",
"keywords": [
"tmUI-vuetify",
+8 -8
View File
@@ -9,20 +9,20 @@
/**用户自定主题,默认主题,不可删除,可通过工具生成覆盖**/
@import "./theme/amber.css";
@import "./theme/blue.css";
@import "./theme/brown.css";
/* @import "./theme/brown.css"; */
@import "./theme/cyan.css";
@import "./theme/green.css";
@import "./theme/indigo.css";
@import "./theme/lime.css";
/* @import "./theme/indigo.css"; */
/* @import "./theme/lime.css"; */
@import "./theme/orange.css";
@import "./theme/pink.css";
@import "./theme/purple.css";
/* @import "./theme/pink.css"; */
/* @import "./theme/purple.css"; */
@import "./theme/red.css";
/**组件库,必要的主题,不可删除**/
@import "./theme/teal.css";
/* @import "./theme/teal.css"; */
@import "./theme/yellow.css";
@import "./theme/blue-grey.css";
@import "./theme/deep-orange.css";
@import "./theme/deep-purple.css";
/*@import "./theme/deep-purple.css";*/
@import "./theme/light-blue.css";
@import "./theme/light-green.css";
/*@import "./theme/light-green.css"; */
@@ -0,0 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).dayjs_plugin_calendar=t()}(this,(function(){"use strict";return function(e,t,a){var n="h:mm A",d={lastDay:"[Yesterday at] "+n,sameDay:"[Today at] "+n,nextDay:"[Tomorrow at] "+n,nextWeek:"dddd [at] "+n,lastWeek:"[Last] dddd [at] "+n,sameElse:"MM/DD/YYYY"};t.prototype.calendar=function(e,t){var n=t||this.$locale().calendar||d,o=a(e||void 0).startOf("d"),s=this.diff(o,"d",!0),i="sameElse",f=s<-6?i:s<-1?"lastWeek":s<0?"lastDay":s<1?"sameDay":s<2?"nextDay":s<7?"nextWeek":i,l=n[f]||d[f];return"function"==typeof l?l.call(this,a()):this.format(l)}}}));
+1
View File
@@ -5,6 +5,7 @@
*/
function sleep(wait=500){
let timid = null;
if(wait==0) return Promise.resolve(true)
clearTimeout(timid);
return new Promise((res,rej)=>{
timid = setTimeout(function() {
+1
View File
@@ -197,4 +197,5 @@ export default {
chinaPost,
password,
password2,
bankCard
}
+4 -4
View File
@@ -19,16 +19,16 @@ class vuex {
let t = this;
const g = this.store.getters
let keys = Object.keys(g);
console.log(keys)
let k = keys.map((el,index)=>{
let f = el.split('/');
let tst = {}
if(f.length==1){
tst[el]=g[el]
}else{
tst[f[0]]={}
tst[f[0]][f[1]] = g[el]
tst[f[1]]=g[el]
// tst[f[0]+'_'+f[1]]=g[el]
// tst[f[0]][f[1]] = g[el]
}
return tst
+1
View File
@@ -55,6 +55,7 @@ const store = new Vuex.Store({
// $tm:state=>{
// return $tm;
// }
},
mutations: {
setTmVuetifyColor(state, color) {
-5
View File
@@ -1,5 +0,0 @@
## 2.3.32022-07-14
1.添加空数据图点击事件回调。
2.修复在nvue中偶现的下拉刷新回弹跳动的问题。
3.修复在nvue中偶现的slot="top"挡住下拉刷新view的问题。
4.禁止在h5中滚动到底部时上拉手势冒泡,以避免由此引发的列表被短暂锁住无法滚动的问题。
@@ -1,34 +0,0 @@
<!-- z-paging -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群790460711 -->
<!-- z-paging-cell用于在nvue中使用cell包裹vue中使用view包裹 -->
<template>
<!-- #ifdef APP-NVUE -->
<cell :style="[cellStyle]">
<slot />
</cell>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<view :style="[cellStyle]">
<slot />
</view>
<!-- #endif -->
</template>
<script>
export default {
name: "z-paging-cell",
props: {
//cellStyle
cellStyle: {
type: Object,
default: function() {
return {}
}
}
}
}
</script>
@@ -1,163 +0,0 @@
<!-- z-paging -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群790460711 -->
<!-- 空数据占位view此组件支持easycom规范可以在项目中直接引用 -->
<template>
<view :class="{'zp-container':true,'zp-container-fixed':emptyViewFixed}" :style="[finalEmptyViewStyle]" @click="emptyViewClick">
<view class="zp-main">
<image v-if="!emptyViewImg.length" class="zp-main-image" :style="[emptyViewImgStyle]" :src="emptyImg" />
<image v-else class="zp-main-image" mode="aspectFit" :style="[emptyViewImgStyle]" :src="emptyViewImg" />
<text class="zp-main-title" :style="[emptyViewTitleStyle]">{{emptyViewText}}</text>
<text v-if="showEmptyViewReload" class="zp-main-error-btn" :style="[emptyViewReloadStyle]" @click.stop="reloadClick">{{emptyViewReloadText}}</text>
</view>
</view>
</template>
<script>
import zStatic from '../z-paging/js/z-paging-static'
export default {
name: "z-paging-empty-view",
data() {
return {
base64Empty: zStatic.base64Empty,
base64Error: zStatic.base64Error
};
},
props: {
//
emptyViewText: {
type: String,
default: '没有数据哦~'
},
//
emptyViewImg: {
type: String,
default: ''
},
//
showEmptyViewReload: {
type: Boolean,
default: false
},
//
emptyViewReloadText: {
type: String,
default: '重新加载'
},
//
isLoadFailed: {
type: Boolean,
default: false
},
//
emptyViewStyle: {
type: Object,
default: function() {
return {}
}
},
//img
emptyViewImgStyle: {
type: Object,
default: function() {
return {}
}
},
//
emptyViewTitleStyle: {
type: Object,
default: function() {
return {}
}
},
//
emptyViewReloadStyle: {
type: Object,
default: function() {
return {}
}
},
//z-index
emptyViewZIndex: {
type: Number,
default: 9
},
//使fixedz-paging
emptyViewFixed: {
type: Boolean,
default: true
}
},
computed: {
emptyImg() {
return this.isLoadFailed ? this.base64Error : this.base64Empty;
},
finalEmptyViewStyle(){
this.emptyViewStyle['z-index'] = this.emptyViewZIndex;
return this.emptyViewStyle;
}
},
methods: {
reloadClick() {
this.$emit('reload');
},
emptyViewClick() {
this.$emit('viewClick');
}
}
}
</script>
<style scoped>
.zp-container{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.zp-container-fixed {
/* #ifndef APP-NVUE */
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
.zp-main{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
padding: 50rpx 0rpx;
}
.zp-main-image {
width: 200rpx;
height: 200rpx;
}
.zp-main-title {
font-size: 26rpx;
color: #aaaaaa;
text-align: center;
margin-top: 10rpx;
}
.zp-main-error-btn {
font-size: 26rpx;
padding: 8rpx 24rpx;
border: solid 1px #dddddd;
border-radius: 6rpx;
color: #aaaaaa;
margin-top: 50rpx;
}
</style>
@@ -1,145 +0,0 @@
<!-- z-paging -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群790460711 -->
<!-- 滑动切换选项卡swiper-item此组件支持easycom规范可以在项目中直接引用 -->
<template>
<view class="zp-swiper-item-container">
<z-paging ref="paging" :fixed="false"
:useVirtualList="useVirtualList" :useInnerList="useInnerList" :cellKeyName="cellKeyName" :innerListStyle="innerListStyle"
:preloadPage="preloadPage" :cellHeightMode="cellHeightMode" :virtualScrollFps="virtualScrollFps" :virtualListCol="virtualListCol"
@query="_queryList" @listChange="_updateList" :mounted-auto-call-reload="false" style="height: 100%;">
<slot />
<template v-slot:header>
<slot name="header"/>
</template>
<template v-slot:cell="{item,index}">
<slot name="cell" :item="item" :index="index"/>
</template>
<template v-slot:footer>
<slot name="footer"/>
</template>
</z-paging>
</view>
</template>
<script>
import zPaging from '../z-paging/z-paging'
export default {
name: "z-paging-swiper-item",
components: {
zPaging
},
data() {
return {
firstLoaded: false
}
},
props: {
//indexswiper
tabIndex: {
type: Number,
default: function() {
return 0
}
},
//swiperindex
currentIndex: {
type: Number,
default: function() {
return 0
}
},
//使
useVirtualList: {
type: Boolean,
default: false
},
//z-paging()use-virtual-listtruetrue
useInnerList: {
type: Boolean,
default: false
},
//cellkeynvuenvueuse-inner-list
cellKeyName: {
type: String,
default: ''
},
//innerList
innerListStyle: {
type: Object,
default: function() {
return {};
}
},
//()77celldom()
preloadPage: {
type: [Number, String],
default: 7
},
//cellfixedcellcelldynamicdynamicfixed
cellHeightMode: {
type: String,
default: 'fixed'
},
//122
virtualListCol: {
type: [Number, String],
default: 1
},
//scroll60
virtualScrollFps: {
type: [Number, String],
default: 60
},
},
watch: {
currentIndex: {
handler(newVal, oldVal) {
if (newVal === this.tabIndex) {
//item
if (!this.firstLoaded) {
this.$nextTick(()=>{
let delay = 5;
// #ifdef MP-TOUTIAO
delay = 100;
// #endif
setTimeout(() => {
this.$refs.paging.reload();
}, delay);
})
}
}
},
immediate: true
}
},
methods: {
reload(data) {
this.$refs.paging.reload(data);
},
complete(data) {
this.firstLoaded = true;
this.$refs.paging.complete(data);
},
_queryList(pageNo, pageSize, from) {
this.$emit('query', pageNo, pageSize, from);
},
_updateList(list) {
this.$emit('updateList', list);
}
}
}
</script>
<style scoped>
.zp-swiper-item-container {
/* #ifndef APP-NVUE */
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
</style>
@@ -1,227 +0,0 @@
<!-- z-paging -->
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
<!-- 反馈QQ群790460711 -->
<!-- 滑动切换选项卡swiper此组件支持easycom规范可以在项目中直接引用 -->
<template>
<view :class="fixed?'zp-swiper-container zp-swiper-container-fixed':'zp-swiper-container'" :style="[finalSwiperStyle]">
<!-- #ifndef APP-PLUS -->
<view v-if="cssSafeAreaInsetBottom===-1" class="zp-safe-area-inset-bottom"></view>
<!-- #endif -->
<slot v-if="$slots.top" name="top" />
<view class="zp-swiper-super">
<view v-if="$slots.left" :class="{'zp-swiper-left':true,'zp-absoulte':isOldWebView}">
<slot name="left" />
</view>
<view :class="{'zp-swiper':true,'zp-absoulte':isOldWebView}" :style="[swiperContentStyle]">
<slot />
</view>
<view v-if="$slots.right" :class="{'zp-swiper-right':true,'zp-absoulte zp-right':isOldWebView}">
<slot name="right" />
</view>
</view>
<slot v-if="$slots.bottom" name="bottom" />
</view>
</template>
<script>
export default {
name: "z-paging-swiper",
data() {
return {
systemInfo: null,
cssSafeAreaInsetBottom: -1,
swiperContentStyle: {}
};
},
props: {
//使fixed
fixed: {
type: Boolean,
default: true
},
//
safeAreaInsetBottom: {
type: Boolean,
default: false
},
//z-paging-swiper
swiperStyle: {
type: Object,
default: function() {
return {};
},
}
},
mounted() {
this.$nextTick(() => {
this.systemInfo = uni.getSystemInfoSync();
})
// #ifndef APP-PLUS
this._getCssSafeAreaInsetBottom();
// #endif
this._updateLeftAndRightWidth();
this.swiperContentStyle = {'flex': '1'};
// #ifndef APP-NVUE
this.swiperContentStyle = {width: '100%',height: '100%'};
// #endif
},
computed: {
finalSwiperStyle() {
let swiperStyle = this.swiperStyle;
if (!this.systemInfo) return swiperStyle;
let windowTop = this.systemInfo.windowTop;
//vue3windowTopbughttps://ask.dcloud.net.cn/question/141634
//litangyuhttps://github.com/SmileZXLee/uni-z-paging/issues/25
// #ifdef VUE3 && H5
const pageHeadNode = document.getElementsByTagName("uni-page-head");
if (!pageHeadNode.length) windowTop = 0;
// #endif
const windowBottom = this.systemInfo.windowBottom;
if (this.fixed) {
if (windowTop && !swiperStyle.top) {
swiperStyle.top = windowTop + 'px';
}
if (!swiperStyle.bottom) {
let bottom = windowBottom ? windowBottom : 0;
if (this.safeAreaInsetBottom) {
bottom += this.safeAreaBottom;
}
if(bottom > 0){
swiperStyle.bottom = bottom + 'px';
}
}
}
return swiperStyle;
},
safeAreaBottom() {
if(!this.systemInfo){
return 0;
}
let safeAreaBottom = 0;
// #ifdef APP-PLUS
safeAreaBottom = this.systemInfo.safeAreaInsets.bottom || 0;
// #endif
// #ifndef APP-PLUS
safeAreaBottom = this.cssSafeAreaInsetBottom === -1 ? 0 : this.cssSafeAreaInsetBottom;
// #endif
return safeAreaBottom;
},
isOldWebView() {
// #ifndef APP-NVUE
try {
const systemInfos = uni.getSystemInfoSync().system.split(' ');
const deviceType = systemInfos[0];
const version = parseInt(systemInfos[1].slice(0,1));
if ((deviceType === 'iOS' && version <= 10) || (deviceType === 'Android' && version <= 6)) {
return true;
}
} catch(e){
return false;
}
// #endif
return false;
}
},
methods: {
//cssviewbottom
_getCssSafeAreaInsetBottom(){
const query = uni.createSelectorQuery().in(this);
query.select('.zp-safe-area-inset-bottom').boundingClientRect(res => {
if (res) {
this.cssSafeAreaInsetBottom = res.height;
}
}).exec();
},
//slot="left"slot="right"
_updateLeftAndRightWidth(){
if (!this.isOldWebView) return;
this.$nextTick(() => {
let delayTime = 0;
// #ifdef MP-BAIDU
delayTime = 10;
// #endif
setTimeout(() => {
const query = uni.createSelectorQuery().in(this);
query.select('.zp-swiper-left').boundingClientRect(res => {
this.swiperContentStyle['left'] = res ? res.width + 'px' : 0;
}).exec();
query.select('.zp-swiper-right').boundingClientRect(res => {
this.swiperContentStyle['right'] = res ? res.width + 'px' : 0;
}).exec();
}, delayTime)
})
}
}
}
</script>
<style scoped>
.zp-swiper-container {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
flex: 1;
}
.zp-swiper-container-fixed {
position: fixed;
/* #ifndef APP-NVUE */
height: auto;
width: auto;
/* #endif */
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.zp-safe-area-inset-bottom {
position: absolute;
/* #ifndef APP-PLUS */
height: env(safe-area-inset-bottom);
/* #endif */
}
.zp-swiper-super {
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.zp-swiper-left,.zp-swiper-right{
/* #ifndef APP-NVUE */
height: 100%;
/* #endif */
}
.zp-swiper {
flex: 1;
/* #ifndef APP-NVUE */
height: 100%;
width: 100%;
/* #endif */
}
.zp-absoulte {
/* #ifndef APP-NVUE */
position: absolute;
top: 0;
width: auto;
/* #endif */
}
.zp-right{
right: 0;
}
.zp-swiper-item {
height: 100%;
}
</style>
@@ -1,152 +0,0 @@
<!-- [z-paging]上拉加载更多view -->
<template>
<view class="zp-l-container" :style="[zConfig.customStyle]" @click="doClick">
<template v-if="!zConfig.hideContent">
<text v-if="zConfig.showNoMoreLine&&finalStatus===2" :class="zConfig.defaultThemeStyle==='white'?'zp-l-line zp-l-line-white':'zp-l-line zp-l-line-black'"
:style="[zConfig.noMoreLineCustomStyle]" />
<!-- #ifndef APP-NVUE -->
<image v-if="finalStatus===1&&zConfig.loadingIconCustomImage.length"
:src="zConfig.loadingIconCustomImage" :style="[zConfig.iconCustomStyle]" :class="{'zp-l-line-loading-custom-image':true,'zp-l-line-loading-custom-image-animated':zConfig.loadingAnimated}" />
<image v-if="finalStatus===1&&zConfig.loadingIconType==='flower'&&!zConfig.loadingIconCustomImage.length"
class="zp-line-loading-image" :style="[zConfig.iconCustomStyle]" :src="zConfig.defaultThemeStyle==='white'?base64FlowerWhite:base64Flower" />
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view>
<loading-indicator v-if="finalStatus===1&&zConfig.loadingIconType!=='circle'" class="zp-line-loading-image" :style="[{color:zConfig.defaultThemeStyle==='white'?'white':'#777777'}]" animating />
</view>
<!-- #endif -->
<text v-if="finalStatus===1&&zConfig.loadingIconType==='circle'&&!zConfig.loadingIconCustomImage.length"
:class="zConfig.defaultThemeStyle==='white'?'zp-l-line-loading-view zp-l-line-loading-view-white':'zp-l-line-loading-view zp-l-line-loading-view-black'" :style="[zConfig.iconCustomStyle]" />
<text :class="zConfig.defaultThemeStyle==='white'?'zp-l-text zp-l-text-white':'zp-l-text zp-l-text-black'" :style="[zConfig.titleCustomStyle]">{{ownLoadingMoreText}}</text>
<text v-if="zConfig.showNoMoreLine&&finalStatus===2" :class="zConfig.defaultThemeStyle==='white'?'zp-l-line zp-l-line-white':'zp-l-line zp-l-line-black'" :style="[zConfig.noMoreLineCustomStyle]" />
</template>
</view>
</template>
<script>
import zStatic from '../js/z-paging-static'
export default {
name: 'z-paging-load-more',
data() {
return {
base64Arrow: zStatic.base64Arrow,
base64Flower: zStatic.base64Flower,
base64FlowerWhite: zStatic.base64FlowerWhite,
};
},
props: ['zConfig'],
computed: {
ownLoadingMoreText() {
return this.statusTextArr[this.finalStatus];
},
statusTextArr() {
return [this.zConfig.defaultText,this.zConfig.loadingText,this.zConfig.noMoreText,this.zConfig.failText];
},
finalStatus() {
if (this.zConfig.defaultAsLoading && this.zConfig.status === 0) return 1;
return this.zConfig.status;
}
},
methods: {
doClick() {
this.$emit('doClick');
}
}
}
</script>
<style scoped>
@import "../css/z-paging-static.css";
.zp-l-container {
height: 80rpx;
font-size: 27rpx;
/* #ifndef APP-NVUE */
clear: both;
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.zp-l-line-loading-custom-image {
color: #a4a4a4;
margin-right: 8rpx;
width: 28rpx;
height: 28rpx;
}
.zp-l-line-loading-custom-image-animated{
/* #ifndef APP-NVUE */
animation: loading-circle 1s linear infinite;
/* #endif */
}
.zp-l-line-loading-view {
margin-right: 8rpx;
width: 23rpx;
height: 23rpx;
border: 3rpx solid #dddddd;
border-radius: 50%;
/* #ifndef APP-NVUE */
animation: loading-circle 1s linear infinite;
/* #endif */
/* #ifdef APP-NVUE */
width: 30rpx;
height: 30rpx;
/* #endif */
}
.zp-l-line-loading-view-black {
border-color: #c8c8c8;
border-top-color: #444444;
}
.zp-l-line-loading-view-white {
border-color: #aaaaaa;
border-top-color: #ffffff;
}
.zp-l-text {
/* #ifdef APP-NVUE */
font-size: 30rpx;
margin: 0rpx 10rpx;
/* #endif */
}
.zp-l-text-black {
color: #a4a4a4;
}
.zp-l-text-white {
color: #efefef;
}
.zp-l-line {
height: 1px;
width: 100rpx;
margin: 0rpx 10rpx;
}
.zp-l-line-black {
background-color: #eeeeee;
}
.zp-l-line-white {
background-color: #efefef;
}
/* #ifndef APP-NVUE */
@keyframes loading-circle {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
/* #endif */
</style>
@@ -1,273 +0,0 @@
<!-- [z-paging]下拉刷新view -->
<template>
<view style="height: 100%;">
<view :class="showUpdateTime?'zp-r-container zp-r-container-padding':'zp-r-container'">
<view class="zp-r-left">
<image v-if="status!==2" :class="leftImageClass"
:style="[{width: showUpdateTime?'36rpx':'30rpx',height: showUpdateTime?'36rpx':'30rpx','margin-right': showUpdateTime?'20rpx':'9rpx'},imgStyle]"
:src="defaultThemeStyle==='white'?(status===3?base64SuccessWhite:base64ArrowWhite):(status===3?base64Success:base64Arrow)" />
<!-- #ifndef APP-NVUE -->
<image v-else class="zp-line-loading-image zp-r-left-image"
:style="[{width: showUpdateTime?'36rpx':'30rpx',height: showUpdateTime?'36rpx':'30rpx','margin-right': showUpdateTime?'20rpx':'9rpx'},imgStyle]"
:src="defaultThemeStyle==='white'?base64FlowerWhite:base64Flower" />
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view v-else :style="[{'margin-right':showUpdateTime?'18rpx':'12rpx'}]">
<loading-indicator :class="systemInfo.platform==='ios'?'zp-loading-image-ios':'zp-loading-image-android'"
:style="[{color:defaultThemeStyle==='white'?'white':'#777777'},imgStyle]" animating />
</view>
<!-- #endif -->
</view>
<view class="zp-r-right">
<text class="zp-r-right-text"
:style="[rightTextStyle,titleStyle]">{{statusTextArr[status]||defaultText}}
</text>
<text v-if="showUpdateTime&&refresherTimeText.length" class="zp-r-right-text zp-r-right-time-text" :style="[rightTextStyle,updateTimeStyle]">{{refresherTimeText}}</text>
</view>
</view>
</view>
</template>
<script>
const systemInfo = uni.getSystemInfoSync();
import zStatic from '../js/z-paging-static'
import u from '../js/z-paging-utils'
export default {
name: 'z-paging-refresh',
data() {
return {
systemInfo: systemInfo,
base64Arrow: zStatic.base64Arrow,
base64ArrowWhite: zStatic.base64ArrowWhite,
base64Flower: zStatic.base64Flower,
base64FlowerWhite: zStatic.base64FlowerWhite,
base64Success: zStatic.base64Success,
base64SuccessWhite: zStatic.base64SuccessWhite,
refresherTimeText: '',
leftImageLoaded: false
};
},
props: {
'status': {
default: 0
},
'defaultThemeStyle': {},
'defaultText': '',
'pullingText': '',
'refreshingText': '',
'completeText': '',
'showUpdateTime': {
default: false
},
'updateTimeKey': '',
'imgStyle': {
default: {}
},
'titleStyle': {
default: {}
},
'updateTimeStyle': {
default: {}
},
},
computed: {
statusTextArr() {
this.updateTime(this.updateTimeKey);
return [this.defaultText,this.pullingText,this.refreshingText,this.completeText];
},
leftImageClass() {
if(this.status === 3){
return 'zp-r-left-image-no-transform .zp-r-left-image-pre-size';
}
let cls = 'zp-r-left-image ';
if (this.status === 0) {
if (this.leftImageLoaded) {
cls += 'zp-r-arrow-down';
} else {
this.leftImageLoaded = true;
cls += 'zp-r-arrow-down-no-duration';
}
} else {
cls += 'zp-r-arrow-top';
}
return cls + ' zp-r-left-image-pre-size';
},
rightTextStyle() {
let stl = {};
let color = '#555555';
if (this.defaultThemeStyle === 'white') {
color = '#efefef';
}
// #ifdef APP-NVUE
if (this.showUpdateTime) {
stl = {
'height': '40rpx',
'line-height': '40rpx'
};
} else {
stl = {
'height': '80rpx',
'line-height': '80rpx'
};
}
// #endif
stl['color'] = color;
return stl;
}
},
methods: {
updateTime(updateTimeKey) {
if (!updateTimeKey) {
updateTimeKey = this.updateTimeKey;
}
if (this.showUpdateTime) {
this.refresherTimeText = u.getRefesrherFormatTimeByKey(updateTimeKey);
}
}
}
}
</script>
<style scoped>
@import "../css/z-paging-static.css";
.zp-r-container {
/* #ifndef APP-NVUE */
display: flex;
height: 100%;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
}
.zp-r-container-padding {
/* #ifdef APP-NVUE */
padding: 15rpx 0rpx;
/* #endif */
}
.zp-r-left {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
overflow: hidden;
/* #ifdef MP-ALIPAY */
margin-top: -4rpx;
/* #endif */
}
.zp-r-left-image {
/* #ifndef APP-NVUE */
transform: rotate(180deg);
margin-top: 2rpx;
/* #endif */
/* #ifdef APP-NVUE */
transition-duration: .2s;
transition-property: transform;
color: #666666;
/* #endif */
}
.zp-r-left-image-no-transform {
/* #ifndef APP-NVUE */
margin-top: 2rpx;
/* #endif */
/* #ifdef APP-NVUE */
transition-duration: .2s;
transition-property: transform;
color: #666666;
/* #endif */
}
.zp-r-left-image-pre-size{
/* #ifndef APP-NVUE */
width: 30rpx;
height: 30rpx;
overflow: hidden;
/* #endif */
}
.zp-r-arrow-top {
/* #ifndef APP-NVUE */
animation: refresher-arrow-top .2s linear;
-webkit-animation: refresher-arrow-top .2s linear;
animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
/* #endif */
/* #ifdef APP-NVUE */
transform: rotate(0deg);
/* #endif */
}
.zp-r-arrow-down {
/* #ifndef APP-NVUE */
animation: refresher-arrow-down .2s linear;
-webkit-animation: refresher-arrow-down .2s linear;
animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
/* #endif */
/* #ifdef APP-NVUE */
transform: rotate(180deg);
/* #endif */
}
.zp-r-arrow-down-no-duration {
/* #ifndef APP-NVUE */
animation: refresher-arrow-down 0s linear;
-webkit-animation: refresher-arrow-down 0s linear;
animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
/* #endif */
/* #ifdef APP-NVUE */
transform: rotate(180deg);
/* #endif */
}
.zp-r-right {
font-size: 27rpx;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
justify-content: center;
}
.zp-r-right-text {
/* #ifdef APP-NVUE */
font-size: 28rpx;
/* #endif */
}
.zp-r-right-time-text {
margin-top: 10rpx;
font-size: 24rpx;
}
/* #ifndef APP-NVUE */
@keyframes refresher-arrow-top {
0% {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
100% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
}
@keyframes refresher-arrow-down {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
}
/* #endif */
</style>
@@ -1,3 +0,0 @@
// z-paging全局配置文件,注意避免更新时此文件被覆盖,若被覆盖,可在此文件中右键->点击本地历史记录,找回覆盖前的配置
export default {}
@@ -1,227 +0,0 @@
/* [z-paging]公共css*/
.z-paging-content {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
height: 100%;
overflow: hidden;
/* #endif */
flex-direction: column;
}
.z-paging-content-fixed, .zp-loading-fixed {
position: fixed;
/* #ifndef APP-NVUE */
height: auto;
width: auto;
/* #endif */
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.zp-page-top,.zp-page-bottom {
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
position: fixed;
left: 0;
right: 0;
z-index: 999;
}
.zp-page-left,.zp-page-right{
/* #ifndef APP-NVUE */
height: 100%;
/* #endif */
}
.zp-scroll-view-super {
flex: 1;
position: relative;
}
.zp-view-super{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.zp-custom-refresher-container {
overflow: hidden;
}
.zp-scroll-view-container,.zp-scroll-view {
position: relative;
/* #ifndef APP-NVUE */
height: 100%;
width: 100%;
/* #endif */
}
.zp-absoulte{
/* #ifndef APP-NVUE */
position: absolute;
top: 0;
width: auto;
/* #endif */
}
.zp-right{
right: 0;
}
.zp-scroll-view-absolute {
position: absolute;
top: 0;
left: 0;
}
/* #ifndef APP-NVUE */
.zp-scroll-view-hide-scrollbar ::-webkit-scrollbar {
display: none;
-webkit-appearance: none;
width: 0 !important;
height: 0 !important;
background: transparent;
}
/* #endif */
.zp-paging-touch-view {
width: 100%;
height: 100%;
position: relative;
}
.zp-fixed-bac-view {
position: absolute;
width: 100%;
top: 0;
left: 0;
height: 200px;
}
.zp-paging-main {
height: 100%;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.zp-paging-container {
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.zp-chat-record-loading-container {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
/* #ifdef APP-NVUE */
width: 750rpx;
/* #endif */
align-items: center;
justify-content: center;
height: 60rpx;
font-size: 26rpx;
}
.zp-chat-record-loading-custom-image {
width: 35rpx;
height: 35rpx;
/* #ifndef APP-NVUE */
animation: loading-flower 1s linear infinite;
/* #endif */
}
.zp-custom-refresher-container {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
}
.zp-back-to-top {
width: 76rpx;
height: 76rpx;
z-index: 999;
position: absolute;
bottom: 0rpx;
right: 25rpx;
transition-duration: .3s;
transition-property: opacity;
}
.zp-back-to-top-show {
opacity: 1;
}
.zp-back-to-top-hide {
opacity: 0;
}
.zp-back-to-top-img {
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
z-index: 999;
}
.zp-empty-view {
/* #ifdef APP-NVUE */
height: 100%;
/* #endif */
flex: 1;
}
.zp-empty-view-center {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
justify-content: center;
}
.zp-loading-fixed {
z-index: 9999;
}
.zp-safe-area-inset-bottom {
position: absolute;
/* #ifndef APP-PLUS */
height: env(safe-area-inset-bottom);
/* #endif */
}
.zp-n-refresh-container {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
width: 750rpx;
}
.zp-n-list-container{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
}
@@ -1,35 +0,0 @@
/* [z-paging]公用的静态css资源 */
.zp-line-loading-image {
margin-right: 8rpx;
width: 28rpx;
height: 28rpx;
/* #ifndef APP-NVUE */
animation: loading-flower 1s steps(12) infinite;
/* #endif */
color: #666666;
}
.zp-loading-image-ios{
width: 20px;
height: 20px;
}
.zp-loading-image-android{
width: 32rpx;
height: 32rpx;
}
/* #ifndef APP-NVUE */
@keyframes loading-flower {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn);
}
}
/* #endif */
@@ -1,108 +0,0 @@
// [z-paging]点击返回顶部view模块
import u from '.././z-paging-utils'
const ZPBackToTop = {
props: {
//自动显示点击返回顶部按钮,默认为否
autoShowBackToTop: {
type: Boolean,
default: u.gc('autoShowBackToTop', false)
},
//点击返回顶部按钮显示/隐藏的阈值(滚动距离),单位为px,默认为400rpx
backToTopThreshold: {
type: [Number, String],
default: u.gc('backToTopThreshold', '400rpx')
},
//点击返回顶部按钮的自定义图片地址,默认使用z-paging内置的图片
backToTopImg: {
type: String,
default: u.gc('backToTopImg', '')
},
//点击返回顶部按钮返回到顶部时是否展示过渡动画,默认为是
backToTopWithAnimate: {
type: Boolean,
default: u.gc('backToTopWithAnimate', true)
},
//点击返回顶部按钮与底部的距离,注意添加单位px或rpx,默认为160rpx
backToTopBottom: {
type: [Number, String],
default: u.gc('backToTopBottom', '160rpx')
},
//点击返回顶部按钮的自定义样式
backToTopStyle: {
type: Object,
default: function() {
return u.gc('backToTopStyle', {});
},
},
//iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只支持竖向,默认为是
enableBackToTop: {
type: Boolean,
default: u.gc('enableBackToTop', true)
},
},
data() {
return {
backToTopClass: 'zp-back-to-top zp-back-to-top-hide',
lastBackToTopShowTime: 0,
showBackToTopClass: false,
}
},
computed: {
finalEnableBackToTop() {
return this.usePageScroll ? false : this.enableBackToTop;
},
finalBackToTopThreshold() {
return u.convertTextToPx(this.backToTopThreshold);
},
finalBackToTopStyle() {
let tempBackToTopStyle = this.backToTopStyle;
if (!tempBackToTopStyle.bottom) {
tempBackToTopStyle.bottom = this.windowBottom + u.convertTextToPx(this.backToTopBottom) + 'px';
}
if(!tempBackToTopStyle.position){
tempBackToTopStyle.position = this.usePageScroll ? 'fixed': 'absolute';
}
return tempBackToTopStyle;
},
},
methods: {
//点击返回顶部
_backToTopClick() {
!this.backToTopWithAnimate && this._checkShouldShowBackToTop(1, 0);
this.scrollToTop(this.backToTopWithAnimate);
},
//判断是否要显示返回顶部按钮
_checkShouldShowBackToTop(newVal, oldVal) {
if (!this.autoShowBackToTop) {
this.showBackToTopClass = false;
return;
}
if (newVal !== oldVal) {
if (newVal > this.finalBackToTopThreshold) {
if (!this.showBackToTopClass) {
this.showBackToTopClass = true;
this.lastBackToTopShowTime = new Date().getTime();
setTimeout(() => {
this.backToTopClass = 'zp-back-to-top zp-back-to-top-show';
}, 300)
}
} else {
if (this.showBackToTopClass) {
const currentTime = new Date().getTime();
let dalayTime = 300;
if(currentTime - this.lastBackToTopShowTime < 500){
dalayTime = 0;
}
this.backToTopClass = 'zp-back-to-top zp-back-to-top-hide';
setTimeout(() => {
this.showBackToTopClass = false;
}, dalayTime)
}
}
}
},
}
}
export default ZPBackToTop;
@@ -1,731 +0,0 @@
// [z-paging]数据处理模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import Enum from '.././z-paging-enum'
import interceptor from '../z-paging-interceptor'
const ZPData = {
props: {
//自定义初始的pageNo,默认为1
defaultPageNo: {
type: [Number, String],
default: u.gc('defaultPageNo', 1),
observer: function(newVal, oldVal) {
this.pageNo = newVal;
},
},
//自定义pageSize,默认为10
defaultPageSize: {
type: [Number, String],
default: u.gc('defaultPageSize', 10),
validator: (value) => {
if(value <= 0) u.consoleErr('default-page-size必须大于0');
return value > 0;
}
},
//为保证数据一致,设置当前tab切换时的标识key,并在complete中传递相同key,若二者不一致,则complete将不会生效
dataKey: {
type: [Number, String, Object],
default: function() {
return u.gc('dataKey', null);
},
},
//自动注入的list名,可自动修改父view(包含ref="paging")中对应name的list值
autowireListName: {
type: String,
default: function() {
return u.gc('autowireListName', '');
},
},
//自动注入的query名,可自动调用父view(包含ref="paging")中的query方法
autowireQueryName: {
type: String,
default: function() {
return u.gc('autowireQueryName', '');
},
},
//z-paging mounted后自动调用reload方法(mounted后自动调用接口),默认为是。请使用简便写法:auto
mountedAutoCallReload: {
type: Boolean,
default: u.gc('mountedAutoCallReload', true)
},
//z-paging mounted后自动调用reload方法(mounted后自动调用接口),默认为是
auto: {
type: Boolean,
default: u.gc('auto', true)
},
//用户下拉刷新时是否触发reload方法,默认为是
reloadWhenRefresh: {
type: Boolean,
default: u.gc('reloadWhenRefresh', true)
},
//reload时自动滚动到顶部,默认为是
autoScrollToTopWhenReload: {
type: Boolean,
default: u.gc('autoScrollToTopWhenReload', true)
},
//reload时立即自动清空原list,默认为是,若立即自动清空,则在reload之后、请求回调之前页面是空白的
autoCleanListWhenReload: {
type: Boolean,
default: u.gc('autoCleanListWhenReload', true)
},
//列表刷新时自动显示下拉刷新view,默认为否
showRefresherWhenReload: {
type: Boolean,
default: u.gc('showRefresherWhenReload', false)
},
//列表刷新时自动显示加载更多view,且为加载中状态,默认为否
showLoadingMoreWhenReload: {
type: Boolean,
default: u.gc('showLoadingMoreWhenReload', false)
},
//组件created时立即触发reload(可解决一些情况下先看到页面再看到loading的问题),auto为true时有效。为否时将在mounted+nextTick后触发reload,默认为否
createdReload: {
type: Boolean,
default: u.gc('createdReload', false)
},
//本地分页时上拉加载更多延迟时间,单位为毫秒,默认200毫秒
localPagingLoadingTime: {
type: [Number, String],
default: u.gc('localPagingLoadingTime', 200)
},
//当分页未满一屏时,是否自动加载更多,默认为否(nvue无效)
insideMore: {
type: Boolean,
default: u.gc('insideMore', false)
},
//使用聊天记录模式,默认为否
useChatRecordMode: {
type: Boolean,
default: u.gc('useChatRecordMode', false)
},
//自动拼接complete中传过来的数组(使用聊天记录模式时无效)
concat: {
type: Boolean,
default: u.gc('concat', true)
},
//父组件v-model所绑定的list的值
value: {
type: Array,
default: function() {
return [];
}
},
// #ifdef VUE3
modelValue: {
type: Array,
default: function() {
return [];
}
}
// #endif
},
data (){
return {
currentData: [],
totalData: [],
realTotalData: [],
totalLocalPagingList: [],
pageNo: 1,
isLocalPaging: false,
isAddedData: false,
isTotalChangeFromAddData: false,
privateConcat: true,
myParentQuery: -1,
firstPageLoaded: false,
pagingLoaded: false,
loaded: false,
isUserReload: true,
fromEmptyViewReload: false,
listRendering: false
}
},
computed: {
pageSize() {
return this.defaultPageSize;
},
finalConcat() {
return this.concat && this.privateConcat;
},
isFirstPage() {
return this.pageNo === this.defaultPageNo;
}
},
watch: {
totalData(newVal, oldVal) {
this._totalDataChange(newVal, oldVal);
},
currentData(newVal, oldVal) {
this._currentDataChange(newVal, oldVal);
},
useChatRecordMode(newVal, oldVal) {
if (newVal) {
this.nLoadingMoreFixedHeight = false;
}
},
value: {
handler(newVal) {
this.realTotalData = newVal;
},
immediate: true
},
// #ifdef VUE3
modelValue: {
handler(newVal) {
this.realTotalData = newVal;
},
immediate: true
}
// #endif
},
methods: {
//请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为是否成功(默认是是)
complete(data, success = true) {
this.customNoMore = -1;
this.addData(data, success);
},
//简写,与complete完全相同
end(data, success = true) {
this.complete(data, success);
},
//【保证数据一致】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为dataKey,需与:data-key绑定的一致,第三个参数为是否成功(默认为是)
completeByKey(data, dataKey = null, success = true) {
if (dataKey !== null && this.dataKey !== null && dataKey !== this.dataKey) {
if (this.isFirstPage) {
this.endRefresh();
}
return;
}
this.customNoMore = -1;
this.addData(data, success);
},
//简写,与completeByKey完全相同
endByKey(data, dataKey = null, success = true) {
this.completeByKey(data, dataKey, success);
},
//【通过totalCount判断是否有更多数据】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为totalCount(列表总数),第三个参数为是否成功(默认为是)
completeByTotalCount(data, totalCount, success = true) {
if (totalCount == 'undefined') {
this.customNoMore = -1;
} else {
let dataTypeRes = this._checkDataType(data, success, false);
data = dataTypeRes.data;
success = dataTypeRes.success;
if (totalCount >= 0 && success) {
this.$nextTick(() => {
let nomore = true;
let realTotalDataCount = this.realTotalData.length;
if (this.pageNo == this.defaultPageNo) {
realTotalDataCount = 0;
}
let exceedCount = realTotalDataCount + data.length - totalCount;
if (exceedCount >= 0) {
nomore = false;
exceedCount = this.defaultPageSize - exceedCount;
if (exceedCount > 0 && exceedCount < data.length) {
data = data.splice(0, exceedCount);
}
}
this.completeByNoMore(data, nomore, success);
})
return;
}
}
this.addData(data, success);
},
//简写,与completeByTotalCount完全相同
completeByTotal(data, totalCount, success = true) {
this.completeByTotalCount(data, totalCount, success);
},
//简写,与completeByTotalCount完全相同
endByTotalCount(data, totalCount, success = true) {
this.completeByTotalCount(data, totalCount, success);
},
//简写,与completeByTotalCount完全相同
endByTotal(data, totalCount, success = true) {
this.completeByTotalCount(data, totalCount, success);
},
//【自行判断是否有更多数据】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为是否有更多数据,第三个参数为是否成功(默认是是)
completeByNoMore(data, nomore, success = true) {
if (nomore != 'undefined') {
this.customNoMore = nomore == true ? 1 : 0;
}
this.addData(data, success);
},
//简写,与completeByNoMore完全相同
endByNoMore(data, nomore, success = true) {
this.completeByNoMore(data, nomore, success);
},
//与上方complete方法功能一致,新版本中设置服务端回调数组请使用complete方法
addData(data, success = true) {
if (!this.fromCompleteEmit) {
this.disabledCompleteEmit = true;
this.fromCompleteEmit = false;
}
const currentTimeStamp = u.getTime();
let addDataDalay = 0;
const disTime = currentTimeStamp - this.requestTimeStamp;
let minDelay = this.minDelay;
if(this.isFirstPage && this.finalShowRefresherWhenReload){
minDelay = Math.max(400,minDelay);
}
if(this.requestTimeStamp > 0 && disTime < minDelay){
addDataDalay = minDelay - disTime;
}
this.$nextTick(() => {
let delay = this.delay > 0 ? this.delay : addDataDalay;
setTimeout(() => {
this._addData(data, success, false);
}, delay)
})
},
//从顶部添加数据,不会影响分页的pageNo和pageSize
addDataFromTop(data, toTop = true, toTopWithAnimate = true) {
let dataType = Object.prototype.toString.call(data);
if (dataType !== '[object Array]') {
data = [data];
}
this.totalData = [...data, ...this.totalData];
if (toTop) {
setTimeout(() => {
this._scrollToTop(toTopWithAnimate);
}, c.delayTime)
}
},
//重新设置列表数据,调用此方法不会影响pageNo和pageSize,也不会触发请求。适用场景:当需要删除列表中某一项时,将删除对应项后的数组通过此方法传递给z-paging。(当出现类似的需要修改列表数组的场景时,请使用此方法,请勿直接修改page中:list.sync绑定的数组)
resetTotalData(data) {
if (data == undefined) {
if (this.showConsoleError) {
u.consoleErr('方法resetTotalData参数缺失!');
}
return;
}
this.isTotalChangeFromAddData = true;
let dataType = Object.prototype.toString.call(data);
if (dataType !== '[object Array]') {
data = [data];
}
this.totalData = data;
},
//添加聊天记录
addChatRecordData(data, toBottom = true, toBottomWithAnimate = true) {
let dataType = Object.prototype.toString.call(data);
if (dataType !== '[object Array]') {
data = [data];
}
if (!this.useChatRecordMode) return;
this.isTotalChangeFromAddData = true;
//#ifndef APP-NVUE
this.totalData = [...this.totalData, ...data];
//#endif
//#ifdef APP-NVUE
this.totalData = this.nIsFirstPageAndNoMore ? [...this.totalData, ...data] : [...data, ...this.totalData];
//#endif
if (toBottom) {
setTimeout(() => {
//#ifndef APP-NVUE
this._scrollToBottom(toBottomWithAnimate);
//#endif
//#ifdef APP-NVUE
if (this.nIsFirstPageAndNoMore) {
this._scrollToBottom(toBottomWithAnimate);
} else {
this._scrollToTop(toBottomWithAnimate);
}
//#endif
}, c.delayTime)
}
},
//设置本地分页数据,请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging作分页处理(若调用了此方法,则上拉加载更多时内部会自动分页,不会触发@query所绑定的事件)
setLocalPaging(data, success = true) {
this.isLocalPaging = true;
this.$nextTick(() => {
this._addData(data, success, true);
})
},
//重新加载分页数据,pageNo会恢复为默认值,相当于下拉刷新的效果(animate为true时会展示下拉刷新动画,默认为false)
reload(animate = this.showRefresherWhenReload) {
if (animate) {
this.privateShowRefresherWhenReload = animate;
this.isUserPullDown = true;
}
this._preReload(animate, false);
},
//刷新列表数据,pageNo和pageSize不会重置,列表数据会重新从服务端获取。必须保证@query绑定的方法中的pageNo和pageSize和传给服务端的一致
refresh() {
if(!this.realTotalData.length){
this.reload();
return;
}
const disPageNo = this.pageNo - this.defaultPageNo + 1;
if (disPageNo >= 1) {
this.loading = true;
this.privateConcat = false;
const totalPageSize = disPageNo * this.pageSize;
this._emitQuery(this.defaultPageNo, totalPageSize, Enum.QueryFrom.Refresh);
this._callMyParentQuery(this.defaultPageNo, totalPageSize);
}
},
//清空分页数据
clean() {
this._reload(true);
this._addData([], true, false);
},
//清空分页数据
clear() {
this.clean();
},
//手动触发滚动到顶部加载更多,聊天记录模式时有效
doChatRecordLoadMore() {
this.useChatRecordMode && this._onLoadingMore('click');
},
//reload之前的一些处理
_preReload(animate = this.showRefresherWhenReload, isFromMounted = true) {
this.isUserReload = true;
this.loadingType = Enum.LoadingType.Refresher;
if (animate) {
this.privateShowRefresherWhenReload = animate;
// #ifndef APP-NVUE
if (this.useCustomRefresher) {
this._doRefresherRefreshAnimate();
} else {
this.refresherTriggered = true;
}
// #endif
// #ifdef APP-NVUE
this.refresherStatus = Enum.Refresher.Loading;
this.refresherRevealStackCount++;
setTimeout(() => {
this._getNodeClientRect('zp-n-refresh-container', false).then((node) => {
if (node) {
let nodeHeight = node[0].height;
this.nShowRefresherReveal = true;
this.nShowRefresherRevealHeight = nodeHeight;
setTimeout(() => {
this._nDoRefresherEndAnimation(0, -nodeHeight, false, false);
setTimeout(() => {
this._nDoRefresherEndAnimation(nodeHeight, 0);
}, 10)
}, 10)
}
this._reload(false, isFromMounted);
this._doRefresherLoad(false);
});
}, 10)
return;
// #endif
} else {
this._refresherEnd(false, false, false, false);
}
this._reload(false, isFromMounted);
},
//重新加载分页数据
_reload(isClean = false, isFromMounted = false, isUserPullDown = false) {
this.isAddedData = false;
this.cacheScrollNodeHeight = -1;
this.insideOfPaging = -1;
this.pageNo = this.defaultPageNo;
this._cleanRefresherEndTimeout();
!this.privateShowRefresherWhenReload && !isClean && this._startLoading(true);
this.firstPageLoaded = true;
this.isTotalChangeFromAddData = false;
this.totalData = [];
if (!isClean) {
this._emitQuery(this.pageNo, this.defaultPageSize, isUserPullDown ? Enum.QueryFrom.UserPullDown : Enum.QueryFrom.Reload);
let delay = 0;
// #ifdef MP-TOUTIAO
delay = 5;
// #endif
setTimeout(() => {
this._callMyParentQuery();
}, delay)
if (!isFromMounted && this.autoScrollToTopWhenReload) {
let checkedNRefresherLoading = true;
// #ifdef APP-NVUE
checkedNRefresherLoading = !this.nRefresherLoading;
// #endif
if (checkedNRefresherLoading) {
this._scrollToTop(false);
}
}
// #ifndef APP-NVUE
if (!this.usePageScroll && this.useChatRecordMode) {
if (this.showConsoleError) {
u.consoleWarn('使用聊天记录模式时,建议使用页面滚动,可将usePageScroll设置为true以启用页面滚动!!');
}
}
// #endif
}
this.$nextTick(() => {
// #ifdef APP-NVUE
this.nShowBottom = this.realTotalData.length > 0;
// #endif
})
},
//处理服务端返回的数组
_addData(data, success, isLocal) {
this.isAddedData = true;
this.fromEmptyViewReload = false;
this.isTotalChangeFromAddData = true;
this.refresherTriggered = false;
!this.useCustomRefresher && uni.stopPullDownRefresh();
// #ifdef APP-NVUE
this.usePageScroll && uni.stopPullDownRefresh();
// #endif
const tempIsUserPullDown = this.isUserPullDown;
if (this.showRefresherUpdateTime && this.isFirstPage) {
u.setRefesrherTime(u.getTime(), this.refresherUpdateTimeKey);
this.tempLanguageUpdateKey = u.getTime();
this.$refs.refresh && this.$refs.refresh.updateTime();
}
if (tempIsUserPullDown && this.isFirstPage) {
this.isUserPullDown = false;
}
let dataTypeRes = this._checkDataType(data, success, isLocal);
data = dataTypeRes.data;
success = dataTypeRes.success;
let delayTime = c.delayTime;
// #ifdef APP-NVUE
if (this.useChatRecordMode) delayTime = 0;
// #endif
this.loadingForNow = false;
setTimeout(() => {
this.pagingLoaded = true;
this.$nextTick(()=>{
this._refresherEnd(delayTime > 0, true, tempIsUserPullDown);
})
}, delayTime)
if (this.isFirstPage) {
this.isLoadFailed = !success;
}
if (success) {
if (!(this.privateConcat === false && this.loadingStatus === Enum.More.NoMore)) {
this.loadingStatus = Enum.More.Default;
}
if (isLocal) {
this.totalLocalPagingList = data;
this._localPagingQueryList(this.defaultPageNo, this.defaultPageSize, 0, (res) => {
this.complete(res);
})
} else {
let dataChangeDelayTime = 0;
// #ifdef APP-NVUE
if (this.privateShowRefresherWhenReload && this.finalNvueListIs === 'waterfall') {
dataChangeDelayTime = 150;
}
// #endif
setTimeout(() => {
this._currentDataChange(data, this.currentData);
}, dataChangeDelayTime)
}
} else {
this._currentDataChange(data, this.currentData);
this.loadingStatus = Enum.More.Fail;
if (this.loadingType === Enum.LoadingType.LoadingMore) {
this.pageNo--;
}
}
},
//所有数据改变时调用
_totalDataChange(newVal, oldVal, eventThrow=true) {
if ((!this.isUserReload || !this.autoCleanListWhenReload) && this.firstPageLoaded && !newVal.length && oldVal.length) {
return;
}
this._doCheckScrollViewShouldFullHeight(newVal);
if(!this.realTotalData.length && !newVal.length){
eventThrow = false;
}
this.realTotalData = newVal;
if (eventThrow) {
this.$emit('input', newVal);
// #ifdef VUE3
this.$emit('update:modelValue', newVal);
// #endif
this.$emit('update:list', newVal);
this.$emit('listChange', newVal);
this._callMyParentList(newVal);
}
this.firstPageLoaded = false;
this.isTotalChangeFromAddData = false;
this.$nextTick(() => {
setTimeout(()=>{
this._getNodeClientRect('.zp-paging-container-content').then((res) => {
if (res) {
this.$emit('contentHeightChanged', res[0].height);
}
});
},this.isIos?100:300)
// #ifdef APP-NVUE
if (this.useChatRecordMode && this.nIsFirstPageAndNoMore && this.isFirstPage && !this.nFirstPageAndNoMoreChecked) {
this.nFirstPageAndNoMoreChecked = true;
this._scrollToBottom(false);
}
// #endif
})
},
//当前数据改变时调用
_currentDataChange(newVal, oldVal) {
newVal = [...newVal];
this.listRendering = true;
this.$nextTick(() => {
setTimeout(() => {
this.listRendering = false;
},50)
})
// #ifndef APP-NVUE
if (this.finalUseVirtualList) {
this._setCellIndex(newVal,this.totalData.length === 0);
}
this.useChatRecordMode && newVal.reverse();
// #endif
if (this.isFirstPage && this.finalConcat) {
this.totalData = [];
}
if (this.customNoMore !== -1) {
if (this.customNoMore === 0 || !newVal.length) {
this.loadingStatus = Enum.More.NoMore;
}
} else {
if (!newVal.length || (newVal.length && newVal.length < this.defaultPageSize)) {
this.loadingStatus = Enum.More.NoMore;
}
}
if (!this.totalData.length) {
if (this.finalConcat) {
// #ifdef APP-NVUE
if(this.useChatRecordMode && this.isFirstPage && this.loadingStatus === Enum.More.NoMore){
newVal.reverse();
}
// #endif
this.totalData = newVal;
}
if (this.useChatRecordMode) {
// #ifndef APP-NVUE
this.$nextTick(() => {
this._scrollToBottom(false);
})
// #endif
}
} else {
if (this.useChatRecordMode) {
// #ifdef APP-NVUE
this.totalData = [...this.totalData, ...newVal];
// #endif
//#ifndef APP-NVUE
const idIndex = newVal.length;
let idIndexStr = `z-paging-${idIndex}`;
this.totalData = [...newVal, ...this.totalData];
if (this.pageNo !== this.defaultPageNo) {
this.privateScrollWithAnimation = 0;
this.$emit('update:chatIndex', idIndex);
setTimeout(() => {
this._scrollIntoView(idIndexStr, 30 + Math.max(0, this.cacheTopHeight), false, () => {
this.$emit('update:chatIndex', 0);
});
}, this.usePageScroll ? 50 : 200)
} else {
this.$nextTick(() => {
this._scrollToBottom(false);
})
}
//#endif
} else {
if (this.finalConcat) {
const currentScrollTop = this.oldScrollTop;
this.totalData = [...this.totalData, ...newVal];
// #ifdef MP-WEIXIN
if (!this.isIos && !this.refresherOnly && !this.usePageScroll && newVal.length) {
this.loadingMoreTimeStamp = u.getTime();
this.$nextTick(()=>{
this.scrollToY(currentScrollTop);
})
}
// #endif
} else {
this.totalData = newVal;
}
}
}
this.privateConcat = true;
},
//本地分页请求
_localPagingQueryList(pageNo, pageSize, localPagingLoadingTime, callback) {
pageNo = parseInt(pageNo);
pageSize = parseInt(pageSize);
if (pageNo < 0 || pageSize <= 0) {
this._localPagingQueryResult(callback, [], localPagingLoadingTime);
return;
}
pageNo = Math.max(1,pageNo);
let totalPagingList = [...this.totalLocalPagingList];
let pageNoIndex = (pageNo - 1) * pageSize;
if (pageNoIndex + pageSize <= totalPagingList.length) {
this._localPagingQueryResult(callback, totalPagingList.splice(pageNoIndex, pageSize), localPagingLoadingTime);
} else if (pageNoIndex < totalPagingList.length) {
this._localPagingQueryResult(callback, totalPagingList.splice(pageNoIndex, totalPagingList.length - pageNoIndex), localPagingLoadingTime);
} else {
this._localPagingQueryResult(callback, [], localPagingLoadingTime);
}
},
//本地分页请求回调
_localPagingQueryResult(callback, arg, localPagingLoadingTime) {
setTimeout(() => {
callback(arg);
}, localPagingLoadingTime)
},
//修改父view的list
_callMyParentList(newVal) {
if (this.autowireListName.length) {
const myParent = u.getParent(this.$parent);
if (myParent && myParent[this.autowireListName]) {
myParent[this.autowireListName] = newVal;
}
}
},
//调用父view的query
_callMyParentQuery(customPageNo = 0, customPageSize = 0) {
if (this.autowireQueryName) {
if (this.myParentQuery === -1) {
const myParent = u.getParent(this.$parent);
if (myParent && myParent[this.autowireQueryName]) {
this.myParentQuery = myParent[this.autowireQueryName];
}
}
if (this.myParentQuery !== -1) {
if (customPageSize > 0) {
this.myParentQuery(customPageNo, customPageSize);
} else {
this.myParentQuery(this.pageNo, this.defaultPageSize);
}
}
}
},
//发射query事件
_emitQuery(pageNo, pageSize, from){
this.requestTimeStamp = u.getTime();
this.$emit('query', ...interceptor._handleQuery(pageNo, pageSize, from));
},
//检查complete data的类型
_checkDataType(data, success, isLocal) {
const dataType = Object.prototype.toString.call(data);
if (dataType === '[object Boolean]') {
success = data;
data = [];
} else if (dataType === '[object Null]') {
data = [];
} else if (dataType !== '[object Array]') {
data = [];
let methodStr = isLocal ? 'setLocalPaging' : 'complete';
if (dataType !== '[object Undefined]') {
if (this.showConsoleError) {
u.consoleErr(`${methodStr}参数类型不正确,第一个参数类型必须为Array!`);
}
}
}
return {data,success};
},
}
}
export default ZPData;
@@ -1,154 +0,0 @@
// [z-paging]空数据图view模块
import u from '.././z-paging-utils'
const ZPEmptyView = {
props: {
//是否强制隐藏空数据图,默认为否
hideEmptyView: {
type: Boolean,
default: u.gc('hideEmptyView', false)
},
//空数据图描述文字,默认为“没有数据哦~”
emptyViewText: {
type: [String, Object],
default: u.gc('emptyViewText', null)
},
//是否显示空数据图重新加载按钮(无数据时),默认为否
showEmptyViewReload: {
type: Boolean,
default: u.gc('showEmptyViewReload', false)
},
//加载失败时是否显示空数据图重新加载按钮,默认为是
showEmptyViewReloadWhenError: {
type: Boolean,
default: u.gc('showEmptyViewReloadWhenError', true)
},
//空数据图点击重新加载文字,默认为“重新加载”
emptyViewReloadText: {
type: [String, Object],
default: u.gc('emptyViewReloadText', null)
},
//空数据图图片,默认使用z-paging内置的图片
emptyViewImg: {
type: String,
default: u.gc('emptyViewImg', '')
},
//空数据图“加载失败”描述文字,默认为“很抱歉,加载失败”
emptyViewErrorText: {
type: [String, Object],
default: u.gc('emptyViewErrorText', null)
},
//空数据图“加载失败”图片,默认使用z-paging内置的图片
emptyViewErrorImg: {
type: String,
default: u.gc('emptyViewErrorImg', '')
},
//空数据图样式
emptyViewStyle: {
type: Object,
default: function() {
return u.gc('emptyViewStyle', {});
}
},
//空数据图容器样式
emptyViewSuperStyle: {
type: Object,
default: function() {
return u.gc('emptyViewSuperStyle', {});
}
},
//空数据图img样式
emptyViewImgStyle: {
type: Object,
default: function() {
return u.gc('emptyViewImgStyle', {});
}
},
//空数据图描述文字样式
emptyViewTitleStyle: {
type: Object,
default: function() {
return u.gc('emptyViewTitleStyle', {});
}
},
//空数据图重新加载按钮样式
emptyViewReloadStyle: {
type: Object,
default: function() {
return u.gc('emptyViewReloadStyle', {});
}
},
//空数据图片是否铺满z-paging,默认为是。若设置为否,则为填充满z-paging的剩余部分
emptyViewFixed: {
type: Boolean,
default: function() {
return u.gc('emptyViewFixed', false)
}
},
//空数据图片是否垂直居中,默认为是。emptyViewFixed为false时有效
emptyViewCenter: {
type: Boolean,
default: function() {
return u.gc('emptyViewCenter', true)
}
},
//加载中时是否自动隐藏空数据图,默认为是
autoHideEmptyViewWhenLoading: {
type: Boolean,
default: u.gc('autoHideEmptyViewWhenLoading', true)
},
//用户下拉列表触发下拉刷新加载中时是否自动隐藏空数据图,默认为是
autoHideEmptyViewWhenPull: {
type: Boolean,
default: u.gc('autoHideEmptyViewWhenPull', true)
},
//空数据view的z-index,默认为9
emptyViewZIndex: {
type: Number,
default: u.gc('emptyViewZIndex', 9)
},
},
computed: {
finalEmptyViewImg() {
return this.isLoadFailed ? this.emptyViewErrorImg : this.emptyViewImg;
},
finalShowEmptyViewReload() {
return this.isLoadFailed ? this.showEmptyViewReloadWhenError : this.showEmptyViewReload;
},
showEmpty() {
if(this.refresherOnly || this.hideEmptyView || this.totalData.length) return false;
if(this.autoHideEmptyViewWhenLoading){
if (this.isAddedData && !this.firstPageLoaded && !this.loading) return true;
}else{
return true;
}
if (!this.autoHideEmptyViewWhenPull && !this.isUserReload) return true;
return false;
},
},
methods: {
//点击了空数据view重新加载按钮
_emptyViewReload() {
let callbacked = false;
this.$emit('emptyViewReload', (reload) => {
if (reload === undefined || reload === true) {
this.fromEmptyViewReload = true;
this.reload();
}
callbacked = true;
});
this.$nextTick(() => {
if (!callbacked) {
this.fromEmptyViewReload = true;
this.reload();
}
})
},
//点击了空数据view
_emptyViewClick() {
this.$emit('emptyViewClick');
},
}
}
export default ZPEmptyView;
@@ -1,98 +0,0 @@
// [z-paging]i18n模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import zI18n from '.././z-paging-i18n'
const systemInfo = uni.getSystemInfoSync();
const ZPI18n = {
props: {
//i18n国际化设置语言,支持简体中文(zh-cn)、繁体中文(zh-hant-cn)和英文(en)
language: {
type: String,
default: u.gc('language', '')
},
//i18n国际化默认是否跟随系统语言,默认为是
followSystemLanguage: {
type: Boolean,
default: u.gc('followSystemLanguage', true)
},
},
data() {
return {
tempLanguageUpdateKey: 0,
}
},
computed: {
tempLanguage() {
let systemLanguage = false;
const temp = this.tempLanguageUpdateKey;
if (this.followSystemLanguage) {
systemLanguage = systemInfo.language;
}
return uni.getStorageSync(c.i18nUpdateKey) || systemLanguage || 'zh-cn';
},
finalTempLanguage() {
return this.language.length ? this.language : this.tempLanguage;
},
finalLanguage() {
let language = this.finalTempLanguage.toLowerCase();
return zI18n._getPrivateLanguage(language, this.followSystemLanguage);
},
finalRefresherDefaultText() {
return this._getI18nText('refresherDefaultText', this.refresherDefaultText);
},
finalRefresherPullingText() {
return this._getI18nText('refresherPullingText', this.refresherPullingText);
},
finalRefresherRefreshingText() {
return this._getI18nText('refresherRefreshingText', this.refresherRefreshingText);
},
finalRefresherCompleteText() {
return this._getI18nText('refresherCompleteText', this.refresherCompleteText);
},
finalLoadingMoreDefaultText() {
return this._getI18nText('loadingMoreDefaultText', this.loadingMoreDefaultText);
},
finalLoadingMoreLoadingText() {
return this._getI18nText('loadingMoreLoadingText', this.loadingMoreLoadingText);
},
finalLoadingMoreNoMoreText() {
return this._getI18nText('loadingMoreNoMoreText', this.loadingMoreNoMoreText);
},
finalLoadingMoreFailText() {
return this._getI18nText('loadingMoreFailText', this.loadingMoreFailText);
},
finalEmptyViewText() {
return this.isLoadFailed ? this.finalEmptyViewErrorText : this._getI18nText('emptyViewText', this.emptyViewText);
},
finalEmptyViewReloadText() {
return this._getI18nText('emptyViewReloadText', this.emptyViewReloadText);
},
finalEmptyViewErrorText() {
return this._getI18nText('emptyViewErrorText', this.emptyViewErrorText);
},
},
methods: {
//设置i18n国际化语言
setI18n(language) {
zI18n.setLanguage(language);
},
//获取当前z-paging的语言
getLanguage() {
return this.finalLanguage;
},
//获取国际化转换后的文本
_getI18nText(key, value) {
const dataType = Object.prototype.toString.call(value);
if (dataType === '[object Object]') {
const nextValue = value[this.finalLanguage];
if (nextValue) return nextValue;
} else if (dataType === '[object String]') {
return value;
}
return zI18n.t[key][this.finalLanguage];
},
}
}
export default ZPI18n;
@@ -1,328 +0,0 @@
// [z-paging]滚动到底部加载更多模块
import u from '.././z-paging-utils'
import Enum from '.././z-paging-enum'
const ZPLoadMore = {
props: {
//自定义底部加载更多样式
loadingMoreCustomStyle: {
type: Object,
default: function() {
return u.gc('loadingMoreCustomStyle', {});
}
},
//自定义底部加载更多文字样式
loadingMoreTitleCustomStyle: {
type: Object,
default: function() {
return u.gc('loadingMoreTitleCustomStyle', {});
}
},
//自定义底部加载更多加载中动画样式
loadingMoreLoadingIconCustomStyle: {
type: Object,
default: function() {
return u.gc('loadingMoreLoadingIconCustomStyle', {});
}
},
//自定义底部加载更多加载中动画图标类型,可选flower或circle,默认为flower
loadingMoreLoadingIconType: {
type: String,
default: u.gc('loadingMoreLoadingIconType', 'flower')
},
//自定义底部加载更多加载中动画图标图片
loadingMoreLoadingIconCustomImage: {
type: String,
default: u.gc('loadingMoreLoadingIconCustomImage', '')
},
//底部加载更多加载中view是否展示旋转动画,默认为是
loadingMoreLoadingAnimated: {
type: Boolean,
default: u.gc('loadingMoreLoadingAnimated', true)
},
//是否启用加载更多数据(含滑动到底部加载更多数据和点击加载更多数据),默认为是
loadingMoreEnabled: {
type: Boolean,
default: u.gc('loadingMoreEnabled', true)
},
//是否启用滑动到底部加载更多数据,默认为是
toBottomLoadingMoreEnabled: {
type: Boolean,
default: u.gc('toBottomLoadingMoreEnabled', true)
},
//滑动到底部状态为默认状态时,以加载中的状态展示,默认为否。若设置为是,可避免滚动到底部看到默认状态然后立刻变为加载中状态的问题,但分页数量未超过一屏时,不会显示【点击加载更多】
loadingMoreDefaultAsLoading: {
type: [Boolean],
default: u.gc('loadingMoreDefaultAsLoading', false)
},
//滑动到底部"默认"文字,默认为【点击加载更多】
loadingMoreDefaultText: {
type: [String, Object],
default: u.gc('loadingMoreDefaultText', null)
},
//滑动到底部"加载中"文字,默认为【正在加载...】
loadingMoreLoadingText: {
type: [String, Object],
default: u.gc('loadingMoreLoadingText', null)
},
//滑动到底部"没有更多"文字,默认为【没有更多了】
loadingMoreNoMoreText: {
type: [String, Object],
default: u.gc('loadingMoreNoMoreText', null)
},
//滑动到底部"加载失败"文字,默认为【加载失败,点击重新加载】
loadingMoreFailText: {
type: [String, Object],
default: u.gc('loadingMoreFailText', null)
},
//当没有更多数据且分页内容未超出z-paging时是否隐藏没有更多数据的view,默认为否
hideLoadingMoreWhenNoMoreAndInsideOfPaging: {
type: Boolean,
default: u.gc('hideLoadingMoreWhenNoMoreAndInsideOfPaging', false)
},
//当没有更多数据且分页数组长度少于这个值时,隐藏没有更多数据的view,默认为0,代表不限制。
hideLoadingMoreWhenNoMoreByLimit: {
type: Number,
default: u.gc('hideLoadingMoreWhenNoMoreByLimit', 0)
},
//是否显示默认的加载更多text,默认为是
showDefaultLoadingMoreText: {
type: Boolean,
default: u.gc('showDefaultLoadingMoreText', true)
},
//是否显示没有更多数据的view
showLoadingMoreNoMoreView: {
type: Boolean,
default: u.gc('showLoadingMoreNoMoreView', true)
},
//是否显示没有更多数据的分割线,默认为是
showLoadingMoreNoMoreLine: {
type: Boolean,
default: u.gc('showLoadingMoreNoMoreLine', true)
},
//自定义底部没有更多数据的分割线样式
loadingMoreNoMoreLineCustomStyle: {
type: Object,
default: function() {
return u.gc('loadingMoreNoMoreLineCustomStyle', {});
},
},
//距底部/右边多远时(单位px),触发 scrolltolower 事件,默认为100rpx
lowerThreshold: {
type: [Number, String],
default: u.gc('lowerThreshold', '100rpx')
},
},
data() {
return {
//底部加载更多状态
loadingStatus: Enum.More.Default,
loadingStatusAfterRender: Enum.More.Default,
loadingMoreTimeStamp: 0,
loadingMoreDefaultSlot: null,
showLoadingMore: false,
customNoMore: -1,
}
},
computed: {
zPagingLoadMoreConfig() {
return {
status: this.loadingStatusAfterRender,
defaultAsLoading: this.loadingMoreDefaultAsLoading,
defaultThemeStyle: this.finalLoadingMoreThemeStyle,
customStyle: this.loadingMoreCustomStyle,
titleCustomStyle: this.loadingMoreTitleCustomStyle,
iconCustomStyle: this.loadingMoreLoadingIconCustomStyle,
loadingIconType: this.loadingMoreLoadingIconType,
loadingIconCustomImage: this.loadingMoreLoadingIconCustomImage,
loadingAnimated: this.loadingMoreLoadingAnimated,
showNoMoreLine: this.showLoadingMoreNoMoreLine,
noMoreLineCustomStyle: this.loadingMoreNoMoreLineCustomStyle,
defaultText: this.finalLoadingMoreDefaultText,
loadingText: this.finalLoadingMoreLoadingText,
noMoreText: this.finalLoadingMoreNoMoreText,
failText: this.finalLoadingMoreFailText,
hideContent: !this.loadingMoreDefaultAsLoading && this.listRendering
};
},
finalLoadingMoreThemeStyle() {
return this.loadingMoreThemeStyle.length ? this.loadingMoreThemeStyle : this.defaultThemeStyle;
},
showLoadingMoreDefault() {
return this._showLoadingMore('Default');
},
showLoadingMoreLoading() {
return this._showLoadingMore('Loading');
},
showLoadingMoreNoMore() {
return this._showLoadingMore('NoMore');
},
showLoadingMoreFail() {
return this._showLoadingMore('Fail');
},
showLoadingMoreCustom() {
return this._showLoadingMore('Custom');
}
},
methods: {
//手动触发上拉加载更多(非必须,可依据具体需求使用)
doLoadMore(type) {
this._onLoadingMore(type);
},
//通过@scroll事件检测是否滚动到了底部
_checkScrolledToBottom(scrollDiff, checked = false) {
if (this.checkScrolledToBottomTimeOut) {
clearTimeout(this.checkScrolledToBottomTimeOut);
this.checkScrolledToBottomTimeOut = null;
}
if (this.cacheScrollNodeHeight === -1) {
this._getNodeClientRect('.zp-scroll-view').then((res) => {
if (res) {
let pageScrollNodeHeight = res[0].height;
this.cacheScrollNodeHeight = pageScrollNodeHeight;
if (scrollDiff - pageScrollNodeHeight <= this.finalLowerThreshold) {
this._onLoadingMore('toBottom');
}
}
});
} else {
if (scrollDiff - this.cacheScrollNodeHeight <= this.finalLowerThreshold) {
this._onLoadingMore('toBottom');
} else if (scrollDiff - this.cacheScrollNodeHeight <= 500 && !checked) {
this.checkScrolledToBottomTimeOut = setTimeout(() => {
this._getNodeClientRect('.zp-scroll-view', true, true).then((res) => {
this.oldScrollTop = res[0].scrollTop;
const newScrollDiff = res[0].scrollHeight - this.oldScrollTop;
this._checkScrolledToBottom(newScrollDiff, true);
})
}, 150)
}
}
},
//触发加载更多时调用,from:0-滑动到底部触发;1-点击加载更多触发
_onLoadingMore(from = 'click') {
if (from === 'toBottom') {
if (!this.scrollToBottomBounceEnabled) {
if (this.scrollEnable) {
this.scrollEnable = false;
this.$nextTick(() => {
this.scrollEnable = true;
})
}
}
//#ifdef APP-VUE || H5
if (this.isIos) {
this.renderPropUsePageScroll = -1;
this.$nextTick(() => {
this.renderPropUsePageScroll = this.usePageScroll;
})
}
//#endif
}
this.$emit('scrolltolower', from);
if (from === 'toBottom' && (!this.toBottomLoadingMoreEnabled || this.useChatRecordMode)) return;
if (this.refresherOnly || !this.loadingMoreEnabled || !(this.loadingStatus === Enum.More.Default || this.loadingStatus === Enum.More.Fail) || this.loading) return;
// #ifdef MP-WEIXIN
if (!this.isIos && !this.refresherOnly && !this.usePageScroll) {
const currentTimestamp = u.getTime();
if (this.loadingMoreTimeStamp > 0 && currentTimestamp - this.loadingMoreTimeStamp < 100) {
this.loadingMoreTimeStamp = 0;
return;
}
}
// #endif
this._doLoadingMore();
},
//处理开始加载更多
_doLoadingMore() {
if (this.pageNo >= this.defaultPageNo && this.loadingStatus !== Enum.More.NoMore) {
this.pageNo++;
this._startLoading(false);
if (this.isLocalPaging) {
this._localPagingQueryList(this.pageNo, this.defaultPageSize, this.localPagingLoadingTime, (res) => {
this.addData(res);
})
} else {
this._emitQuery(this.pageNo, this.defaultPageSize, Enum.QueryFrom.LoadingMore);
this._callMyParentQuery();
}
this.loadingType = Enum.LoadingType.LoadingMore;
}
},
//(预处理)判断当没有更多数据且分页内容未超出z-paging时是否显示没有更多数据的view
_preCheckShowLoadingMoreWhenNoMoreAndInsideOfPaging(newVal, scrollViewNode, pagingContainerNode) {
if (this.loadingStatus === Enum.More.NoMore && this.hideLoadingMoreWhenNoMoreByLimit > 0 && newVal.length) {
this.showLoadingMore = newVal.length > this.hideLoadingMoreWhenNoMoreByLimit;
} else if ((this.loadingStatus === Enum.More.NoMore && this.hideLoadingMoreWhenNoMoreAndInsideOfPaging && newVal.length) || (this.insideMore && this.insideOfPaging !== false && newVal.length)) {
this.$nextTick(() => {
this._checkShowLoadingMoreWhenNoMoreAndInsideOfPaging(newVal, scrollViewNode, pagingContainerNode);
})
if (this.insideMore && this.insideOfPaging !== false && newVal.length) {
this.showLoadingMore = newVal.length;
}
} else {
this.showLoadingMore = newVal.length;
}
},
//判断当没有更多数据且分页内容未超出z-paging时是否显示没有更多数据的view
async _checkShowLoadingMoreWhenNoMoreAndInsideOfPaging(totalData, oldScrollViewNode, oldPagingContainerNode) {
try {
const scrollViewNode = oldScrollViewNode || await this._getNodeClientRect('.zp-scroll-view');
if (this.usePageScroll) {
if (scrollViewNode) {
const scrollViewTotalH = scrollViewNode[0].top + scrollViewNode[0].height;
this.insideOfPaging = scrollViewTotalH < this.windowHeight;
if (this.hideLoadingMoreWhenNoMoreAndInsideOfPaging) {
this.showLoadingMore = !this.insideOfPaging;
}
this._updateInsideOfPaging();
}
} else {
let pagingContainerH = 0;
let scrollViewH = 0;
const pagingContainerNode = oldPagingContainerNode || await this._getNodeClientRect('.zp-paging-container-content');
if (pagingContainerNode) {
pagingContainerH = pagingContainerNode[0].height;
}
if (scrollViewNode) {
scrollViewH = scrollViewNode[0].height;
}
this.insideOfPaging = pagingContainerH < scrollViewH;
if (this.hideLoadingMoreWhenNoMoreAndInsideOfPaging) {
this.showLoadingMore = !this.insideOfPaging;
}
this._updateInsideOfPaging();
}
} catch (e) {
this.insideOfPaging = !totalData.length;
if (this.hideLoadingMoreWhenNoMoreAndInsideOfPaging) {
this.showLoadingMore = !this.insideOfPaging;
}
this._updateInsideOfPaging();
}
},
//是否要展示上拉加载更多view
_showLoadingMore(type) {
if (!this.showLoadingMoreWhenReload && (!(this.loadingStatus === Enum.More.Default ? this.nShowBottom : true) || !this.totalData.length)) return false;
if (((!this.showLoadingMoreWhenReload || this.isUserPullDown || this.loadingStatus !== Enum.More.Loading) && !this.showLoadingMore) ||
(!this.loadingMoreEnabled && (!this.showLoadingMoreWhenReload || this.isUserPullDown || this.loadingStatus !== Enum.More.Loading)) ||
this.refresherOnly) return false;
if (this.useChatRecordMode && type !== 'Loading') return false;
if (!this.$slots) return false;
if (type === 'Custom') {
return this.showDefaultLoadingMoreText && !(this.loadingStatus === Enum.More.NoMore && !this.showLoadingMoreNoMoreView);
}
const res = this.loadingStatus === Enum.More[type] && this.$slots[`loadingMore${type}`] && (type === 'NoMore' ? this.showLoadingMoreNoMoreView : true);
if (res) {
// #ifdef APP-NVUE
if (!this.isIos) {
this.nLoadingMoreFixedHeight = false;
}
// #endif
}
return res;
},
}
}
export default ZPLoadMore;
@@ -1,220 +0,0 @@
// [z-paging]nvue独有部分模块
import u from '.././z-paging-utils'
import Enum from '.././z-paging-enum'
// #ifdef APP-NVUE
const weexAnimation = weex.requireModule('animation');
// #endif
const ZPNvue = {
props: {
//nvue中修改列表类型,可选值有list、waterfall和scroller,默认为list
nvueListIs: {
type: String,
default: u.gc('nvueListIs', 'list')
},
//nvue waterfall配置,仅在nvue中且nvueListIs=waterfall时有效,配置参数详情参见:https://uniapp.dcloud.io/component/waterfall
nvueWaterfallConfig: {
type: Object,
default: function() {
return u.gc('nvueWaterfallConfig', {});
}
},
//nvue 控制是否回弹效果,iOS不支持动态修改
nvueBounce: {
type: Boolean,
default: u.gc('nvueBounce', true)
},
//nvue中通过代码滚动到顶部/底部时,是否加快动画效果(无滚动动画时无效),默认为否
nvueFastScroll: {
type: Boolean,
default: u.gc('nvueFastScroll', false)
},
//nvue中list的id
nvueListId: {
type: String,
default: u.gc('nvueListId', '')
},
//nvue中refresh组件的样式
nvueRefresherStyle: {
type: Object,
default: function() {
return u.gc('nvueRefresherStyle', {});
}
},
//nvue中是否按分页模式(类似竖向swiper)显示List,默认为false
nvuePagingEnabled: {
type: Boolean,
default: u.gc('nvuePagingEnabled', false)
},
//是否隐藏nvue列表底部的tagView,此view用于标识滚动到底部位置,若隐藏则滚动到底部功能将失效,在nvue中实现吸顶+swiper功能时需将最外层z-paging的此属性设置为true。默认为否
hideNvueBottomTag: {
type: Boolean,
default: u.gc('hideNvueBottomTag', false)
},
},
data() {
return {
nRefresherLoading: false,
nListIsDragging: false,
nShowBottom: true,
nFixFreezing: false,
nShowRefresherReveal: false,
nIsFirstPageAndNoMore: false,
nFirstPageAndNoMoreChecked: false,
nLoadingMoreFixedHeight: false,
nShowRefresherRevealHeight: 0,
nRefresherWidth: uni.upx2px(750),
}
},
watch: {
nIsFirstPageAndNoMore: {
handler(newVal) {
const cellStyle = !this.useChatRecordMode || newVal ? {} : {transform: 'rotate(180deg)'};
this.$emit('update:cellStyle', cellStyle);
},
immediate: true
}
},
computed: {
// #ifdef APP-NVUE
nWaterfallColumnCount() {
if (this.finalNvueListIs !== 'waterfall') return 0;
return this._nGetWaterfallConfig('column-count', 2);
},
nWaterfallColumnWidth() {
return this._nGetWaterfallConfig('column-width', 'auto');
},
nWaterfallColumnGap() {
return this._nGetWaterfallConfig('column-gap', 'normal');
},
nWaterfallLeftGap() {
return this._nGetWaterfallConfig('left-gap', 0);
},
nWaterfallRightGap() {
return this._nGetWaterfallConfig('right-gap', 0);
},
nViewIs() {
const is = this.finalNvueListIs;
return is === 'scroller' || is === 'view' ? 'view' : is === 'waterfall' ? 'header' : 'cell';
},
nSafeAreaBottomHeight() {
return this.safeAreaInsetBottom ? this.safeAreaBottom : 0;
},
finalNvueListIs() {
if (this.usePageScroll) return 'view';
const nvueListIsLowerCase = this.nvueListIs.toLowerCase();
if (['list','waterfall','scroller'].indexOf(nvueListIsLowerCase) !== -1) {
return nvueListIsLowerCase;
}
return 'list';
},
finalNvueSuperListIs() {
return this.usePageScroll ? 'view' : 'scroller';
},
finalNvueRefresherEnabled() {
return this.finalNvueListIs !== 'view' && this.finalRefresherEnabled && !this.nShowRefresherReveal && !this.useChatRecordMode;
},
// #endif
},
methods: {
// #ifdef APP-NVUE
//列表滚动时触发
_nOnScroll(e) {
this.$emit('scroll', e);
const contentOffsetY = -e.contentOffset.y;
this.oldScrollTop = contentOffsetY;
this.nListIsDragging = e.isDragging;
this._checkShouldShowBackToTop(contentOffsetY, contentOffsetY - 1);
},
//下拉刷新刷新中
_nOnRrefresh() {
if (this.nShowRefresherReveal) return;
this.nRefresherLoading = true;
this.refresherStatus = Enum.Refresher.Loading;
this._doRefresherLoad();
},
//下拉刷新下拉中
_nOnPullingdown(e) {
if (this.refresherStatus === Enum.Refresher.Loading || (this.isIos && !this.nListIsDragging)) return;
this._emitTouchmove(e);
const viewHeight = e.viewHeight;
const pullingDis = e.pullingDistance;
this.refresherStatus = pullingDis >= viewHeight ? Enum.Refresher.ReleaseToRefresh : Enum.Refresher.Default;
},
//下拉刷新结束
_nRefresherEnd(doEnd=true) {
if (doEnd) {
this._nDoRefresherEndAnimation(0, -this.nShowRefresherRevealHeight);
!this.usePageScroll && this.$refs['zp-n-list'].resetLoadmore();
this.nRefresherLoading = false;
}
this.$nextTick(() => {
setTimeout(()=> {
this.nShowBottom = true;
}, 10);
})
},
//执行主动触发下拉刷新动画
_nDoRefresherEndAnimation(height, translateY, animate = true, checkStack = true) {
this._cleanRefresherCompleteTimeout();
this._cleanRefresherEndTimeout();
if (!this.finalShowRefresherWhenReload) {
this.refresherEndTimeout = setTimeout(() => {
this.refresherStatus = Enum.Refresher.Default;
}, this.refresherCompleteDuration);
return;
}
const stackCount = this.refresherRevealStackCount;
if (height === 0 && checkStack) {
this.refresherRevealStackCount--;
if (stackCount > 1) return;
this.refresherEndTimeout = setTimeout(() => {
this.refresherStatus = Enum.Refresher.Default;
}, this.refresherCompleteDuration);
}
if (stackCount > 1) {
this.refresherStatus = Enum.Refresher.Loading;
}
const duration = animate ? 180 : 0;
weexAnimation.transition(this.$refs['zp-n-list-refresher-reveal'], {
styles: {
height: `${height}px`,
transform: `translateY(${translateY}px)`,
},
duration: duration,
timingFunction: 'linear',
needLayout: true,
delay: 0
})
setTimeout(() => {
if (animate) {
this.nShowRefresherReveal = height > 0;
}
}, duration > 0 ? duration - 100 : 0);
},
//滚动到底部加载更多
_nOnLoadmore() {
if (this.nShowRefresherReveal || !this.totalData.length) return;
this.useChatRecordMode ? this.doChatRecordLoadMore() : this._onLoadingMore('toBottom');
},
//获取nvue waterfall单项配置
_nGetWaterfallConfig(key, defaultValue) {
return this.nvueWaterfallConfig[key] || defaultValue;
},
//更新nvue 下拉刷新view容器的宽度
_nUpdateRefresherWidth() {
this.$nextTick(()=>{
this._getNodeClientRect('.zp-n-list').then(node => {
if (node) {
const nodeWidth = node[0].width;
this.nRefresherWidth = nodeWidth;
}
})
})
}
// #endif
}
}
export default ZPNvue;
@@ -1,632 +0,0 @@
// [z-paging]下拉刷新view模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import Enum from '.././z-paging-enum'
const ZPRefresher = {
props: {
//下拉刷新的主题样式,支持blackwhite,默认black
refresherThemeStyle: {
type: String,
default: function() {
return u.gc('refresherThemeStyle', '');
}
},
//自定义下拉刷新中左侧图标的样式
refresherImgStyle: {
type: Object,
default: function() {
return u.gc('refresherImgStyle', {});
}
},
//自定义下拉刷新中右侧状态描述文字的样式
refresherTitleStyle: {
type: Object,
default: function() {
return u.gc('refresherTitleStyle', {});
}
},
//自定义下拉刷新中右侧最后更新时间文字的样式(show-refresher-update-time为true时有效)
refresherUpdateTimeStyle: {
type: Object,
default: function() {
return u.gc('refresherUpdateTimeStyle', {});
}
},
//在微信小程序和QQ小程序中,是否实时监听下拉刷新中进度,默认为否
watchRefresherTouchmove: {
type: Boolean,
default: u.gc('watchRefresherTouchmove', false)
},
//底部加载更多的主题样式,支持black,white,默认black
loadingMoreThemeStyle: {
type: String,
default: function() {
return u.gc('loadingMoreThemeStyle', '');
}
},
//是否只使用下拉刷新,设置为true后将关闭mounted自动请求数据、关闭滚动到底部加载更多,强制隐藏空数据图。默认为否
refresherOnly: {
type: Boolean,
default: u.gc('refresherOnly', false)
},
//自定义下拉刷新默认状态下回弹动画时间,单位为毫秒,默认为100毫秒,nvue无效
refresherDefaultDuration: {
type: [Number, String],
default: u.gc('refresherDefaultDuration', 100)
},
//自定义下拉刷新结束以后延迟回弹的时间,单位为毫秒,默认为0
refresherCompleteDelay: {
type: [Number, String],
default: u.gc('refresherCompleteDelay', 0)
},
//自定义下拉刷新结束回弹动画时间,单位为毫秒,默认为300毫秒(refresherEndBounceEnabled为false时,refresherCompleteDuration为设定值的1/3)nvue无效
refresherCompleteDuration: {
type: [Number, String],
default: u.gc('refresherCompleteDuration', 300)
},
//自定义下拉刷新结束状态下是否允许列表滚动,默认为否
refresherCompleteScrollable: {
type: Boolean,
default: u.gc('refresherCompleteScrollable', false)
},
//是否使用自定义的下拉刷新,默认为是,即使用z-paging的下拉刷新。设置为false即代表使用uni scroll-view自带的下拉刷新,h5、App、微信小程序以外的平台不支持uni scroll-view自带的下拉刷新
useCustomRefresher: {
type: Boolean,
default: u.gc('useCustomRefresher', true)
},
//自定义下拉刷新下拉帧率,默认为40,过高可能会出现抖动问题
refresherFps: {
type: [Number, String],
default: u.gc('refresherFps', 40)
},
//自定义下拉刷新允许触发的最大下拉角度,默认为40度,当下拉角度小于设定值时,自定义下拉刷新动画不会被触发
refresherMaxAngle: {
type: [Number, String],
default: u.gc('refresherMaxAngle', 40)
},
//自定义下拉刷新的角度由未达到最大角度变到达到最大角度时,是否继续下拉刷新手势,默认为否
refresherAngleEnableChangeContinued: {
type: Boolean,
default: u.gc('refresherAngleEnableChangeContinued', false)
},
//自定义下拉刷新默认状态下的文字
refresherDefaultText: {
type: [String, Object],
default: u.gc('refresherDefaultText', null)
},
//自定义下拉刷新松手立即刷新状态下的文字
refresherPullingText: {
type: [String, Object],
default: u.gc('refresherPullingText', null)
},
//自定义下拉刷新刷新中状态下的文字
refresherRefreshingText: {
type: [String, Object],
default: u.gc('refresherRefreshingText', null)
},
//自定义下拉刷新刷新结束状态下的文字
refresherCompleteText: {
type: [String, Object],
default: u.gc('refresherCompleteText', null)
},
//是否开启自定义下拉刷新刷新结束回弹效果,默认为是
refresherEndBounceEnabled: {
type: Boolean,
default: u.gc('refresherEndBounceEnabled', true)
},
//是否开启自定义下拉刷新,默认为是
refresherEnabled: {
type: Boolean,
default: u.gc('refresherEnabled', true)
},
//设置自定义下拉刷新阈值,默认为80rpx
refresherThreshold: {
type: [Number, String],
default: u.gc('refresherThreshold', '80rpx')
},
//设置系统下拉刷新默认样式,支持设置 black,white,none,none 表示不使用默认样式,默认为black
refresherDefaultStyle: {
type: String,
default: u.gc('refresherDefaultStyle', 'black')
},
//设置自定义下拉刷新区域背景
refresherBackground: {
type: String,
default: u.gc('refresherBackground', 'transparent')
},
//设置固定的自定义下拉刷新区域背景
refresherFixedBackground: {
type: String,
default: u.gc('refresherFixedBackground', 'transparent')
},
//设置固定的自定义下拉刷新区域高度,默认为0
refresherFixedBacHeight: {
type: [Number, String],
default: u.gc('refresherFixedBacHeight', 0)
},
//设置自定义下拉刷新下拉超出阈值后继续下拉位移衰减的比例,范围0-1,值越大代表衰减越多。默认为0.7(nvue无效)
refresherOutRate: {
type: Number,
default: u.gc('refresherOutRate', 0.7)
},
//是否显示最后更新时间,默认为否
showRefresherUpdateTime: {
type: Boolean,
default: u.gc('showRefresherUpdateTime', false)
},
//如果需要区别不同页面的最后更新时间,请为不同页面的z-paging的`refresher-update-time-key`设置不同的字符串
refresherUpdateTimeKey: {
type: String,
default: u.gc('refresherUpdateTimeKey', 'default')
},
},
data() {
return {
//下拉刷新状态
refresherStatus: Enum.Refresher.Default,
refresherTouchstartY: 0,
lastRefresherTouchmove: null,
refresherReachMaxAngle: true,
refresherTransform: 'translateY(0px)',
refresherTransition: '',
finalRefresherDefaultStyle: 'black',
refresherRevealStackCount: 0,
refresherCompleteTimeout: null,
refresherCompleteSubTimeout: null,
refresherEndTimeout: null,
isTouchmovingTimeout: null,
refresherTriggered: false,
isTouchmoving: false,
isTouchEnded: false,
isUserPullDown: false,
privateRefresherEnabled: -1,
privateShowRefresherWhenReload: false,
customRefresherHeight: -1,
showCustomRefresher: false,
doRefreshAnimateAfter: false,
isRefresherInComplete: false,
pullDownTimeStamp: 0,
moveDis: 0,
oldMoveDis: 0,
oldRefresherTouchmoveY: 0,
oldTouchDirection: ''
}
},
watch: {
refresherDefaultStyle: {
handler(newVal) {
if (newVal.length) {
this.finalRefresherDefaultStyle = newVal;
}
},
immediate: true
},
refresherStatus(newVal, oldVal) {
if (newVal === Enum.Refresher.Loading){
this._cleanRefresherEndTimeout();
}
this.$emit('refresherStatusChange', newVal);
this.$emit('update:refresherStatus', newVal);
},
moveDis(newVal, oldVal){
this.oldMoveDis = oldVal;
}
},
computed: {
pullDownDisTimeStamp() {
return 1000 / this.refresherFps;
},
finalRefresherEnabled() {
if (this.useChatRecordMode) return false;
if (this.privateRefresherEnabled === -1) return this.refresherEnabled;
return this.privateRefresherEnabled === 1;
},
finalRefresherThreshold() {
let refresherThreshold = this.refresherThreshold;
let idDefault = false;
if (refresherThreshold === '80rpx') {
idDefault = true;
if (this.showRefresherUpdateTime) {
refresherThreshold = '120rpx';
}
}
if (idDefault && this.customRefresherHeight > 0) {
return this.customRefresherHeight;
}
return u.convertTextToPx(refresherThreshold);
},
finalRefresherFixedBacHeight() {
return u.convertTextToPx(this.refresherFixedBacHeight);
},
finalRefresherThemeStyle() {
return this.refresherThemeStyle.length ? this.refresherThemeStyle : this.defaultThemeStyle;
},
finalRefresherOutRate() {
let rate = this.refresherOutRate;
rate = Math.max(0,rate);
rate = Math.min(1,rate);
return rate;
},
finalRefresherTransform() {
if (this.refresherTransform === 'translateY(0px)') return 'none';
return this.refresherTransform;
},
finalShowRefresherWhenReload() {
return this.showRefresherWhenReload || this.privateShowRefresherWhenReload;
},
finalRefresherTriggered() {
if (!(this.finalRefresherEnabled && !this.useCustomRefresher)) return false;
return this.refresherTriggered;
},
showRefresher() {
const showRefresher = this.finalRefresherEnabled && this.useCustomRefresher && this.isTouchmoving;
// #ifndef APP-NVUE
if (this.customRefresherHeight === -1 && showRefresher) {
setTimeout(() => {
this.$nextTick(()=>{
this._updateCustomRefresherHeight();
})
}, 100)
}
// #endif
return showRefresher;
},
hasTouchmove(){
// #ifdef VUE2
// #ifdef APP-VUE || H5
if (this.$listeners && !this.$listeners.refresherTouchmove) return false;
// #endif
// #ifdef MP-WEIXIN || MP-QQ
return this.watchRefresherTouchmove;
// #endif
return true;
// #endif
return this.watchRefresherTouchmove;
},
},
methods: {
//终止下拉刷新状态
endRefresh(){
this._refresherEnd();
},
handleRefresherStatusChanged(func) {
this.refresherStatusChangedFunc = func;
},
//自定义下拉刷新被触发
_onRefresh(fromScrollView=false,isUserPullDown=true) {
if (fromScrollView && !(this.finalRefresherEnabled && !this.useCustomRefresher)) return;
this.$emit('onRefresh');
this.$emit('Refresh');
// #ifdef APP-NVUE
if (this.loading) {
setTimeout(()=>{
this._nRefresherEnd();
},500)
return;
}
// #endif
if (this.loading || this.isRefresherInComplete) return;
this.loadingType = Enum.LoadingType.Refresher;
if (this.nShowRefresherReveal) return;
this.isUserPullDown = isUserPullDown;
this.isUserReload = !isUserPullDown;
this._startLoading(true);
this.refresherTriggered = true;
if(this.reloadWhenRefresh && isUserPullDown){
if (this.useChatRecordMode) {
this._onLoadingMore('click')
} else {
this._reload(false, false, isUserPullDown);
}
}
},
//自定义下拉刷新被复位
_onRestore() {
this.refresherTriggered = 'restore';
this.$emit('onRestore');
this.$emit('Restore');
},
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
//拖拽开始
_refresherTouchstart(e) {
if (this._touchDisabled()) return;
const touch = u.getTouch(e);
this._handleRefresherTouchstart(touch);
},
// #endif
//进一步处理拖拽开始结果
_handleRefresherTouchstart(touch) {
if (!this.loading && this.isTouchEnded) {
this.isTouchmoving = false;
}
this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
this.isTouchEnded = false;
this.refresherTransition = '';
this.refresherTouchstartY = touch.touchY;
this.$emit('refresherTouchstart', this.refresherTouchstartY);
this.lastRefresherTouchmove = touch;
this._cleanRefresherCompleteTimeout();
this._cleanRefresherEndTimeout();
},
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
//拖拽中
_refresherTouchmove(e) {
const currentTimeStamp = u.getTime();
let touch = null;
let refresherTouchmoveY = 0;
if (this.watchTouchDirectionChange) {
touch = u.getTouch(e);
refresherTouchmoveY = touch.touchY;
const direction = refresherTouchmoveY > this.oldRefresherTouchmoveY ? 'top' : 'bottom';
direction === this.oldTouchDirection && this._handleTouchDirectionChange({direction});
this.oldTouchDirection = direction;
this.oldRefresherTouchmoveY = refresherTouchmoveY;
}
if (this.pullDownTimeStamp && currentTimeStamp - this.pullDownTimeStamp <= this.pullDownDisTimeStamp) return;
if (this._touchDisabled()) return;
this.pullDownTimeStamp = Number(currentTimeStamp);
touch = u.getTouch(e);
refresherTouchmoveY = touch.touchY;
let moveDis = refresherTouchmoveY - this.refresherTouchstartY;
if (moveDis < 0) return;
if (this.refresherMaxAngle >= 0 && this.refresherMaxAngle <= 90 && this.lastRefresherTouchmove && this.lastRefresherTouchmove.touchY <= refresherTouchmoveY) {
if (!moveDis && !this.refresherAngleEnableChangeContinued && this.moveDis < 1 && !this.refresherReachMaxAngle) return;
const x = Math.abs(touch.touchX - this.lastRefresherTouchmove.touchX);
const y = Math.abs(refresherTouchmoveY - this.lastRefresherTouchmove.touchY);
const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if ((x || y) && x > 1) {
const angle = Math.asin(y / z) / Math.PI * 180;
if (angle < this.refresherMaxAngle) {
this.lastRefresherTouchmove = touch;
this.refresherReachMaxAngle = false;
return;
}
}
}
moveDis = this._getFinalRefresherMoveDis(moveDis);
this._handleRefresherTouchmove(moveDis, touch);
if (!this.disabledBounce) {
if(this.isIos){
// #ifndef MP-LARK
this._handleScrollViewDisableBounce({bounce: false});
// #endif
}
this.disabledBounce = true;
}
this._emitTouchmove({pullingDistance:moveDis,dy:this.moveDis - this.oldMoveDis});
},
// #endif
//进一步处理拖拽中结果
_handleRefresherTouchmove(moveDis, touch) {
this.refresherReachMaxAngle = true;
this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
this.isTouchmoving = true;
//this.refresherTransition = '';
this.isTouchEnded = false;
this.refresherStatus = moveDis >= this.finalRefresherThreshold ? Enum.Refresher.ReleaseToRefresh : this.refresherStatus = Enum.Refresher.Default;
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
// this.scrollEnable = false;
this.refresherTransform = `translateY(${moveDis}px)`;
this.lastRefresherTouchmove = touch;
// #endif
this.moveDis = moveDis;
},
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
//拖拽结束
_refresherTouchend(e) {
if (this._touchDisabled() || !this.isTouchmoving) return;
const touch = u.getTouch(e);
let refresherTouchendY = touch.touchY;
let moveDis = refresherTouchendY - this.refresherTouchstartY;
moveDis = this._getFinalRefresherMoveDis(moveDis);
this._handleRefresherTouchend(moveDis);
this._handleScrollViewDisableBounce({bounce: true});
this.disabledBounce = false;
},
// #endif
//进一步处理拖拽结束结果
_handleRefresherTouchend(moveDis) {
// #ifndef APP-PLUS || H5 || MP-WEIXIN
if (!this.isTouchmoving) return;
// #endif
this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
this.refresherReachMaxAngle = true;
if (moveDis < 0 && this.usePageScroll && this.loadingMoreEnabled && this.useCustomRefresher && this.pageScrollTop === -1) {
if (this.showConsoleError) {
u.consoleErr('usePageScroll为true并且自定义下拉刷新时必须引入mixin或在page滚动时通过调用z-paging组件的updatePageScrollTop方法设置当前的scrollTop');
}
}
this.isTouchEnded = true;
if (moveDis >= this.finalRefresherThreshold && this.refresherStatus === Enum.Refresher.ReleaseToRefresh) {
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.refresherTransform = `translateY(${this.finalRefresherThreshold}px)`;
// #endif
this.moveDis = this.finalRefresherThreshold;
this.refresherStatus = Enum.Refresher.Loading;
this._doRefresherLoad();
} else {
this._refresherEnd();
this.isTouchmovingTimeout = setTimeout(() => {
this.isTouchmoving = false;
}, this.refresherDefaultDuration);
}
this.scrollEnable = true;
this.$emit('refresherTouchend', moveDis);
},
//处理scroll-view bounce是否生效
_handleScrollViewDisableBounce(e) {
if (!this.usePageScroll && !this.scrollToTopBounceEnabled) {
this.refresherTransition = '';
this.scrollEnable = e.bounce;
}
},
//wxs正在下拉状态改变处理
_handleWxsPullingDownStatusChange(onPullingDown) {
this.wxsOnPullingDown = onPullingDown;
if (onPullingDown && !this.useChatRecordMode) {
this.renderPropScrollTop = 0;
}
},
//wxs正在下拉处理
_handleWxsPullingDown(e){
this._emitTouchmove({pullingDistance:e.moveDis,dy:e.diffDis});
},
//wxs触摸方向改变
_handleTouchDirectionChange(e) {
this.$emit('touchDirectionChange',e.direction);
},
//wxs通知更新其props
_handlePropUpdate(e){
this.wxsPropType = u.getTime().toString();
},
//下拉刷新结束
_refresherEnd(shouldEndLoadingDelay = true, fromAddData = false, isUserPullDown = false, setLoading = true) {
if (this.loadingType === Enum.LoadingType.Refresher) {
let refresherCompleteDelay = 0;
if(fromAddData && (isUserPullDown || this.showRefresherWhenReload)){
refresherCompleteDelay = this.refresherCompleteDuration > 700 ? 1 : this.refresherCompleteDelay;
}
const refresherStatus = refresherCompleteDelay > 0 ? Enum.Refresher.Complete : Enum.Refresher.Default;
if (this.finalShowRefresherWhenReload) {
const stackCount = this.refresherRevealStackCount;
this.refresherRevealStackCount--;
if (stackCount > 1) return;
}
this._cleanRefresherEndTimeout();
this.refresherEndTimeout = setTimeout(() => {
this.refresherStatus = refresherStatus;
}, this.refresherStatus !== Enum.Refresher.Default && refresherStatus === Enum.Refresher.Default ? this.refresherCompleteDuration : 0);
// #ifndef APP-NVUE
if (refresherCompleteDelay > 0) {
this.isRefresherInComplete = true;
}
// #endif
this._cleanRefresherCompleteTimeout();
this.refresherCompleteTimeout = setTimeout(() => {
let animateDuration = 1;
const animateType = this.refresherEndBounceEnabled && fromAddData ? 'cubic-bezier(0.19,1.64,0.42,0.72)' : 'linear';
if (fromAddData) {
animateDuration = this.refresherEndBounceEnabled ? this.refresherCompleteDuration / 1000 : this.refresherCompleteDuration / 3000;
}
this.refresherTransition = `transform ${fromAddData ? animateDuration : this.refresherDefaultDuration / 1000}s ${animateType}`;
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.refresherTransform = 'translateY(0px)';
// #endif
// #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.wxsPropType = this.refresherTransition + 'end' + u.getTime();
// #endif
// #ifdef APP-NVUE
this._nRefresherEnd();
// #endif
this.moveDis = 0;
// #ifndef APP-NVUE
if (refresherStatus === Enum.Refresher.Complete) {
if (this.refresherCompleteSubTimeout) {
clearTimeout(this.refresherCompleteSubTimeout);
this.refresherCompleteSubTimeout = null;
}
this.refresherCompleteSubTimeout = setTimeout(() => {
this.$nextTick(() => {
this.refresherStatus = Enum.Refresher.Default;
this.isRefresherInComplete = false;
})
}, animateDuration * 800);
}
// #endif
}, refresherCompleteDelay);
}
if (setLoading) {
setTimeout(() => {
this.loading = false;
}, shouldEndLoadingDelay ? c.delayTime : 0);
isUserPullDown && this._onRestore();
}
},
//模拟用户手动触发下拉刷新
_doRefresherRefreshAnimate() {
this._cleanRefresherCompleteTimeout();
// #ifndef APP-NVUE
const doRefreshAnimateAfter = !this.doRefreshAnimateAfter && (this.finalShowRefresherWhenReload) && this
.customRefresherHeight === -1 && this.refresherThreshold === '80rpx';
if (doRefreshAnimateAfter) {
this.doRefreshAnimateAfter = true;
return;
}
// #endif
this.refresherRevealStackCount++;
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.refresherTransform = `translateY(${this.finalRefresherThreshold}px)`;
// #endif
// #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
this.wxsPropType = 'begin' + u.getTime();
// #endif
this.moveDis = this.finalRefresherThreshold;
this.refresherStatus = Enum.Refresher.Loading;
this.isTouchmoving = true;
this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
this._doRefresherLoad(false);
},
//触发下拉刷新
_doRefresherLoad(isUserPullDown=true) {
this._onRefresh(false,isUserPullDown);
this.loading = true;
},
//获取处理后的moveDis
_getFinalRefresherMoveDis(moveDis) {
moveDis = moveDis * 0.85;
if (moveDis >= this.finalRefresherThreshold) {
moveDis = this.finalRefresherThreshold + (moveDis - this.finalRefresherThreshold) * (1 - this.finalRefresherOutRate);
}
return moveDis;
},
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
//判断touch手势是否要触发
_touchDisabled() {
let checkOldScrollTop = this.oldScrollTop > 5;
return this.loading || this.isRefresherInComplete || this.useChatRecordMode || !this.refresherEnabled || !this.useCustomRefresher ||(this.usePageScroll && this.useCustomRefresher && this.pageScrollTop > 10) || (!(this.usePageScroll && this.useCustomRefresher) && checkOldScrollTop);
},
// #endif
//更新自定义下拉刷新view高度
_updateCustomRefresherHeight() {
this._getNodeClientRect('.zp-custom-refresher-slot-view').then((res) => {
if (res) {
this.customRefresherHeight = res[0].height;
if (this.customRefresherHeight > 0) {
this.showCustomRefresher = true;
}
} else {
this.customRefresherHeight = 0;
}
if (this.doRefreshAnimateAfter) {
this.doRefreshAnimateAfter = false;
this._doRefresherRefreshAnimate();
}
});
},
//发射pullingDown事件
_emitTouchmove(e){
// #ifndef APP-NVUE
e.viewHeight = this.finalRefresherThreshold;
// #endif
e.rate = e.pullingDistance / e.viewHeight;
if(this.hasTouchmove){
this.$emit('refresherTouchmove',e);
}
},
//清除refresherCompleteTimeout
_cleanRefresherCompleteTimeout() {
this.refresherCompleteTimeout = this._cleanTimeout(this.refresherCompleteTimeout);
// #ifdef APP-NVUE
this._nRefresherEnd(false);
// #endif
},
//清除refresherEndTimeout
_cleanRefresherEndTimeout() {
this.refresherEndTimeout = this._cleanTimeout(this.refresherEndTimeout);
},
}
}
export default ZPRefresher;
@@ -1,478 +0,0 @@
// [z-paging]scroll相关模块
import u from '.././z-paging-utils'
import Enum from '.././z-paging-enum'
// #ifdef APP-NVUE
const weexDom = weex.requireModule('dom');
// #endif
const ZPScroller = {
props: {
//使用页面滚动,默认为否,当设置为是时则使用页面的滚动而非此组件内部的scroll-view的滚动,使用页面滚动时z-paging无需设置确定的高度且对于长列表展示性能更高,但配置会略微繁琐
usePageScroll: {
type: Boolean,
default: u.gc('usePageScroll', false)
},
//是否可以滚动,使用内置scroll-view和nvue时有效,默认为是
scrollable: {
type: Boolean,
default: u.gc('scrollable', true)
},
//控制是否出现滚动条,默认为是
showScrollbar: {
type: Boolean,
default: u.gc('showScrollbar', true)
},
//是否允许横向滚动,默认为否
scrollX: {
type: Boolean,
default: u.gc('scrollX', false)
},
//iOS设备上滚动到顶部时是否允许回弹效果,默认为否。关闭回弹效果后可使滚动到顶部与下拉刷新更连贯,但是有吸顶view时滚动到顶部时可能出现抖动。
scrollToTopBounceEnabled: {
type: Boolean,
default: u.gc('scrollToTopBounceEnabled', false)
},
//iOS设备上滚动到底部时是否允许回弹效果,默认为是。
scrollToBottomBounceEnabled: {
type: Boolean,
default: u.gc('scrollToBottomBounceEnabled', true)
},
//在设置滚动条位置时使用动画过渡,默认为否
scrollWithAnimation: {
type: Boolean,
default: u.gc('scrollWithAnimation', false)
},
//值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素
scrollIntoView: {
type: String,
default: u.gc('scrollIntoView', '')
},
},
data() {
return {
scrollTop: 0,
oldScrollTop: 0,
scrollViewStyle: {},
scrollViewContainerStyle: {},
scrollViewInStyle: {},
pageScrollTop: -1,
scrollEnable: true,
privateScrollWithAnimation: -1,
cacheScrollNodeHeight: -1
}
},
watch: {
oldScrollTop(newVal, oldVal) {
!this.usePageScroll && this._scrollTopChange(newVal,oldVal,false);
},
pageScrollTop(newVal, oldVal) {
this.usePageScroll && this._scrollTopChange(newVal,oldVal,true);
},
usePageScroll: {
handler(newVal) {
this.$nextTick(() => {
this.renderPropUsePageScroll = newVal;
})
if (this.loaded && this.autoHeight) {
this._setAutoHeight(!newVal);
}
// #ifdef H5
if (newVal) {
this.$nextTick(()=>{
const mainScrollRef = this.$refs['zp-scroll-view'].$refs.main;
if (mainScrollRef) {
mainScrollRef.style = {};
}
})
}
// #endif
},
immediate: true
},
finalScrollTop(newVal, oldVal) {
if (!this.useChatRecordMode) {
this.renderPropScrollTop = newVal < 6 ? 0 : 10;
}
},
},
computed: {
finalScrollWithAnimation() {
if (this.privateScrollWithAnimation !== -1) {
const scrollWithAnimation = this.privateScrollWithAnimation === 1;
this.privateScrollWithAnimation = -1;
return scrollWithAnimation;
}
return this.scrollWithAnimation;
},
finalScrollViewStyle() {
if (this.superContentZIndex != 1) {
this.scrollViewStyle['z-index'] = this.superContentZIndex;
this.scrollViewStyle['position'] = 'relative';
}
return this.scrollViewStyle;
},
finalScrollTop() {
return this.usePageScroll ? this.pageScrollTop : this.oldScrollTop;
},
finalIsOldWebView() {
return this.isOldWebView && !this.usePageScroll;
}
},
methods: {
//滚动到顶部,animate为是否展示滚动动画,默认为是
scrollToTop(animate,checkReverse = true) {
// #ifdef APP-NVUE
if (checkReverse && this.useChatRecordMode) {
if(!this.nIsFirstPageAndNoMore){
this.scrollToBottom(animate, false);
return;
}
}
// #endif
this.$nextTick(() => {
this._scrollToTop(animate, false);
// #ifdef APP-NVUE
if (this.nvueFastScroll && animate) {
setTimeout(() => {
this._scrollToTop(false, false);
}, 150);
}
// #endif
})
},
//滚动到底部,animate为是否展示滚动动画,默认为是
scrollToBottom(animate,checkReverse = true) {
// #ifdef APP-NVUE
if (checkReverse && this.useChatRecordMode) {
if(!this.nIsFirstPageAndNoMore){
this.scrollToTop(animate, false);
return;
}
}
// #endif
this.$nextTick(() => {
this._scrollToBottom(animate);
// #ifdef APP-NVUE
if (this.nvueFastScroll && animate) {
setTimeout(() => {
this._scrollToBottom(false);
}, 150);
}
// #endif
})
},
//滚动到指定view(vue中有效)。sel为需要滚动的view的id值,不包含"#"offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollIntoViewById(sel, offset, animate) {
this._scrollIntoView(sel, offset, animate);
},
//滚动到指定view(vue中有效)。nodeTop为需要滚动的view的top值(通过uni.createSelectorQuery()获取)offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollIntoViewByNodeTop(nodeTop, offset, animate) {
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this._scrollIntoViewByNodeTop(nodeTop, offset, animate);
})
},
//滚动到指定位置(vue中有效)。y为与顶部的距离,单位为px;offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollToY(y, offset, animate) {
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this._scrollToY(y, offset, animate);
})
},
//滚动到指定view(nvue中有效)。index为需要滚动的view的index(第几个)offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollIntoViewByIndex(index, offset, animate) {
this._scrollIntoView(index, offset, animate);
},
//滚动到指定view(nvue中有效)。view为需要滚动的view(通过`this.$refs.xxx`获取),不包含"#"offset为偏移量,单位为px;animate为是否展示滚动动画,默认为否
scrollIntoViewByView(view, offset, animate) {
this._scrollIntoView(view, offset, animate);
},
//当使用页面滚动并且自定义下拉刷新时,请在页面的onPageScroll中调用此方法,告知z-paging当前的pageScrollTop,否则会导致在任意位置都可以下拉刷新
updatePageScrollTop(value) {
if (value == undefined) {
u.consoleErr('updatePageScrollTop方法缺少参数,请将页面onPageScroll事件中的scrollTop传递给此方法');
return;
}
this.pageScrollTop = value;
},
//当使用页面滚动并且设置了slot="top"时,默认初次加载会自动获取其高度,并使内部容器下移,当slot="top"的view高度动态改变时,在其高度需要更新时调用此方法
updatePageScrollTopHeight() {
this._updatePageScrollTopOrBottomHeight('top');
},
//当使用页面滚动并且设置了slot="bottom"时,默认初次加载会自动获取其高度,并使内部容器下移,当slot="bottom"的view高度动态改变时,在其高度需要更新时调用此方法
updatePageScrollBottomHeight() {
this._updatePageScrollTopOrBottomHeight('bottom');
},
//更新z-paging内置scroll-view的scrollTop
updateScrollViewScrollTop(scrollTop, animate = true) {
this.privateScrollWithAnimation = animate ? 1 : 0;
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this.scrollTop = scrollTop;
this.oldScrollTop = this.scrollTop;
});
},
//当滚动到顶部时
_scrollToUpper() {
this.$emit('scrolltoupper');
this.$emit('scrollTopChange', 0);
this.$nextTick(() => {
this.oldScrollTop = 0;
})
if (!this.useChatRecordMode) return;
if (this.loadingStatus === Enum.More.NoMore) return;
this._onLoadingMore('click');
},
//滚动到顶部
_scrollToTop(animate = true, isPrivate = true) {
// #ifdef APP-NVUE
const el = this.$refs['zp-n-list-top-tag'];
if (this.usePageScroll) {
this._getNodeClientRect('zp-page-scroll-top', false).then((node) => {
let nodeHeight = 0;
if (node) {
nodeHeight = node[0].height;
}
weexDom.scrollToElement(el, {
offset: -nodeHeight,
animated: animate
});
});
} else {
if(!this.isIos && this.nvueListIs === 'scroller'){
this._getNodeClientRect('zp-n-refresh-container', false).then((node) => {
let nodeHeight = 0;
if (node) {
nodeHeight = node[0].height;
}
weexDom.scrollToElement(el, {
offset: -nodeHeight,
animated: animate
});
});
}else{
weexDom.scrollToElement(el, {
offset: 0,
animated: animate
});
}
}
return;
// #endif
if (this.usePageScroll) {
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: 0,
duration: animate ? 100 : 0,
});
});
return;
}
this.privateScrollWithAnimation = animate ? 1 : 0;
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this.scrollTop = 0;
this.oldScrollTop = this.scrollTop;
});
},
//滚动到底部
async _scrollToBottom(animate = true) {
// #ifdef APP-NVUE
const el = this.$refs['zp-n-list-bottom-tag'];
if (el) {
weexDom.scrollToElement(el, {
offset: 0,
animated: animate
});
} else {
u.consoleErr('滚动到底部失败,因为您设置了hideNvueBottomTag为true');
}
return;
// #endif
if (this.usePageScroll) {
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: Number.MAX_VALUE,
duration: animate ? 100 : 0,
});
});
return;
}
try {
this.privateScrollWithAnimation = animate ? 1 : 0;
let pagingContainerH = 0;
let scrollViewH = 0;
const pagingContainerNode = await this._getNodeClientRect('.zp-paging-container');
const scrollViewNode = await this._getNodeClientRect('.zp-scroll-view');
if (pagingContainerNode) {
pagingContainerH = pagingContainerNode[0].height;
}
if (scrollViewNode) {
scrollViewH = scrollViewNode[0].height;
}
if (pagingContainerH > scrollViewH) {
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
this.scrollTop = pagingContainerH - scrollViewH + this.virtualPlaceholderTopHeight;
this.oldScrollTop = this.scrollTop;
});
}
} catch (e) {}
},
//滚动到指定view
_scrollIntoView(sel, offset = 0, animate = false, finishCallback) {
try {
this.scrollTop = this.oldScrollTop;
this.$nextTick(() => {
// #ifdef APP-NVUE
const refs = this.$parent.$refs;
if (!refs) return;
const dataType = Object.prototype.toString.call(sel);
let el = null;
if (dataType === '[object Number]') {
const els = refs[`z-paging-${sel}`];
el = els ? els[0] : null;
} else if (dataType === '[object Array]') {
el = sel[0];
} else {
el = sel;
}
if (el) {
weexDom.scrollToElement(el, {
offset: -offset,
animated: animate
});
} else {
u.consoleErr('在nvue中滚动到指定位置,cell必须设置 :ref="`z-paging-${index}`"');
}
return;
// #endif
if (sel.indexOf('#') != -1) {
sel = sel.replace('#', '');
}
this._getNodeClientRect('#' + sel, false).then((node) => {
if (node) {
let nodeTop = node[0].top;
this._scrollIntoViewByNodeTop(nodeTop, offset, animate);
if (finishCallback) {
finishCallback();
}
}
});
});
} catch (e) {}
},
//通过nodeTop滚动到指定view
_scrollIntoViewByNodeTop(nodeTop, offset = 0, animate = false) {
this._scrollToY(nodeTop,offset,animate,true);
},
//滚动到指定位置
_scrollToY(y, offset = 0, animate = false, addScrollTop = false) {
this.privateScrollWithAnimation = animate ? 1 : 0;
if (this.usePageScroll) {
uni.pageScrollTo({
scrollTop: y - offset,
duration: animate ? 100 : 0
});
} else {
if(addScrollTop){
y += this.oldScrollTop;
}
this.scrollTop = y - offset;
this.oldScrollTop = this.scrollTop;
}
},
//scroll-view滚动中
_scroll(e) {
this.$emit('scroll', e);
const scrollTop = e.detail.scrollTop;
// #ifndef APP-NVUE
this.finalUseVirtualList && this._updateVirtualScroll(scrollTop,this.oldScrollTop - scrollTop);
// #endif
this.oldScrollTop = scrollTop;
const scrollDiff = e.detail.scrollHeight - this.oldScrollTop;
!this.isIos && this._checkScrolledToBottom(scrollDiff);
},
//scrollTop改变时触发
_scrollTopChange(newVal,oldVal,isPageScrollTop){
this.$emit('scrollTopChange', newVal);
this.$emit('update:scrollTop', newVal);
this._checkShouldShowBackToTop(newVal, oldVal);
const scrollTop = this.isIos ? (newVal > 5 ? 6 : 0) : newVal;
if (isPageScrollTop) {
this.wxsPageScrollTop = scrollTop;
} else {
this.wxsScrollTop = scrollTop;
}
},
//更新使用页面滚动时slot="top"或"bottom"插入view的高度
_updatePageScrollTopOrBottomHeight(type) {
// #ifndef APP-NVUE
if (!this.usePageScroll) return;
// #endif
this._doCheckScrollViewShouldFullHeight(this.realTotalData);
const node = `.zp-page-${type}`;
const marginText = `margin${type.slice(0,1).toUpperCase() + type.slice(1)}`;
let safeAreaInsetBottomAdd = this.safeAreaInsetBottom;
// #ifdef APP-NVUE
if (!this.usePageScroll) {
safeAreaInsetBottomAdd = false;
}
// #endif
this.$nextTick(() => {
let delayTime = 0;
// #ifdef MP-BAIDU || APP-NVUE
delayTime = 50;
// #endif
setTimeout(() => {
this._getNodeClientRect(node).then((res) => {
if (res) {
let pageScrollNodeHeight = res[0].height;
if (type === 'bottom') {
if (safeAreaInsetBottomAdd) {
pageScrollNodeHeight += this.safeAreaBottom;
}
} else {
this.cacheTopHeight = pageScrollNodeHeight;
}
this.$set(this.scrollViewStyle, marginText,
`${pageScrollNodeHeight}px`);
} else if (safeAreaInsetBottomAdd) {
this.$set(this.scrollViewStyle, marginText, `${this.safeAreaBottom}px`);
}
});
}, delayTime)
})
},
//获取slot="left"和slot="right"宽度并且更新布局
_updateLeftAndRightWidth(){
if (!this.finalIsOldWebView) return;
this.$nextTick(() => {
let delayTime = 0;
// #ifdef MP-BAIDU
delayTime = 10;
// #endif
setTimeout(() => {
this._getNodeClientRect('.zp-page-left').then((res) => {
this.scrollViewContainerStyle['left'] = res ? res[0].width + 'px' : 0;
});
this._getNodeClientRect('.zp-page-right').then((res) => {
this.scrollViewContainerStyle['right'] = res ? res[0].width + 'px' : 0;
});
}, delayTime)
})
},
//更新renderJs数据
_updateRenderJsData(){
this.renderPropUsePageScroll = -1;
this.renderPropUsePageScroll = this.usePageScroll;
if (!this.useChatRecordMode) {
this.renderPropScrollTop = -1;
this.renderPropScrollTop = this.finalScrollTop < 6 ? 0 : 10;
}
}
}
}
export default ZPScroller;
@@ -1,374 +0,0 @@
// [z-paging]虚拟列表模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import Enum from '.././z-paging-enum'
const ZPVirtualList = {
props: {
//是否使用虚拟列表,默认为否
useVirtualList: {
type: Boolean,
default: u.gc('useVirtualList', false)
},
//是否在z-paging内部循环渲染列表(内置列表),默认为否。若use-virtual-list为true,则此项恒为true
useInnerList: {
type: Boolean,
default: u.gc('useInnerList', false)
},
//强制关闭inner-list,默认为false,如果为true将强制关闭innerList,适用于开启了虚拟列表后需要强制关闭inner-list的情况
forceCloseInnerList: {
type: Boolean,
default: u.gc('forceCloseInnerList', false)
},
//内置列表cell的key名称,仅nvue有效,在nvue中开启use-inner-list时必须填此项
cellKeyName: {
type: String,
default: u.gc('cellKeyName', '')
},
//innerList样式
innerListStyle: {
type: Object,
default: function() {
return u.gc('innerListStyle', {});
}
},
//innerCell样式
innerCellStyle: {
type: Object,
default: function() {
return u.gc('innerCellStyle', {});
}
},
//预加载的列表可视范围(列表高度)页数,默认为7,即预加载当前页及上下各7页的cell。此数值越大,则虚拟列表中加载的dom越多,内存消耗越大(会维持在一个稳定值),但增加预加载页面数量可缓解快速滚动短暂白屏问题
preloadPage: {
type: [Number, String],
default: u.gc('preloadPage', 7),
validator: (value) => {
if (value <= 0) u.consoleErr('preload-page必须大于0');
return value > 0;
}
},
//虚拟列表cell高度模式,默认为fixed,也就是每个cell高度完全相同,将以第一个cell高度为准进行计算。可选值【dynamic】,即代表高度是动态非固定的,【dynamic】性能低于【fixed】。
cellHeightMode: {
type: String,
default: u.gc('cellHeightMode', 'fixed')
},
//虚拟列表列数,默认为1。常用于每行有多列的情况,例如每行有2列数据,需要将此值设置为2
virtualListCol: {
type: [Number, String],
default: u.gc('virtualListCol', 1)
},
//虚拟列表scroll取样帧率,默认为60,过高可能出现卡顿等问题
virtualScrollFps: {
type: [Number, String],
default: u.gc('virtualScrollFps', 60)
},
},
data() {
return {
virtualListKey: u.getInstanceId(),
virtualPageHeight: 0,
virtualCellHeight: 0,
virtualScrollTimeStamp: 0,
virtualList: [],
virtualPlaceholderTopHeight: 0,
virtualPlaceholderBottomHeight: 0,
virtualTopRangeIndex: 0,
virtualBottomRangeIndex: 0,
lastVirtualTopRangeIndex: 0,
lastVirtualBottomRangeIndex: 0,
virtualHeightCacheList: [],
getCellHeightRetryCount: {
fixed: 0,
dynamic: 0
},
pagingOrgTop: -1,
updateVirtualListFromDataChange: false
}
},
watch: {
realTotalData(newVal) {
// #ifndef APP-NVUE
if (this.finalUseVirtualList) {
this.updateVirtualListFromDataChange = true;
this.$nextTick(() => {
if (!newVal.length) {
this._resetDynamicListState(!this.isUserPullDown);
}
this.getCellHeightRetryCount.fixed = 0;
this.finalUseVirtualList && newVal.length && this.cellHeightMode === Enum.CellHeightMode.Fixed && this.isFirstPage && this._updateFixedCellHeight();
this.finalUseVirtualList && this._updateVirtualScroll(this.oldScrollTop);
})
}
// #endif
},
virtualList(newVal){
this.$emit('update:virtualList', newVal);
this.$emit('virtualListChange', newVal);
}
},
computed: {
finalUseVirtualList() {
if (this.useVirtualList && this.usePageScroll){
u.consoleErr('使用页面滚动时,开启虚拟列表无效!');
}
return this.useVirtualList && !this.usePageScroll;
},
finalUseInnerList() {
return this.useInnerList || (this.finalUseVirtualList && !this.forceCloseInnerList)
},
finalCellKeyName() {
// #ifdef APP-NVUE
if (this.finalUseVirtualList){
if (!this.cellKeyName.length){
u.consoleErr('在nvue中开启use-virtual-list必须设置cell-key-name,否则将可能导致列表渲染错误!');
}
}
// #endif
return this.cellKeyName;
},
finalVirtualPageHeight(){
return this.virtualPageHeight > 0 ? this.virtualPageHeight : this.windowHeight;
return virtualPageHeight * this.preloadPage;
},
virtualRangePageHeight(){
return this.finalVirtualPageHeight * this.preloadPage;
},
virtualScrollDisTimeStamp() {
return 1000 / this.virtualScrollFps;
},
},
methods: {
//初始化虚拟列表
_virtualListInit() {
this.$nextTick(() => {
setTimeout(() => {
this._getNodeClientRect('.zp-scroll-view').then(node => {
if (node && node.length) {
this.pagingOrgTop = node[0].top;
this.virtualPageHeight = node[0].height;
}
});
}, c.delayTime);
})
},
//cellHeightMode为fixed时获取第一个cell高度
_updateFixedCellHeight() {
this.$nextTick(() => {
const updateFixedCellHeightTimeout = setTimeout(() => {
this._getNodeClientRect(`#zp-${0}`,this.finalUseInnerList).then(cellNode => {
const hasCellNode = cellNode && cellNode.length;
if (!hasCellNode) {
clearTimeout(updateFixedCellHeightTimeout);
if (this.getCellHeightRetryCount.fixed > 10) {
u.consoleErr('获取虚拟列表cell高度失败,可能是for循环cell处没有写:id="`zp-${item.zp_index}`",请检查您的代码!')
return;
}
this.getCellHeightRetryCount.fixed++;
this._updateFixedCellHeight();
} else {
this.virtualCellHeight = cellNode[0].height;
this._updateVirtualScroll(this.oldScrollTop);
}
});
}, c.delayTime);
})
},
//cellHeightMode为dynamic时获取每个cell高度
_updateDynamicCellHeight(list) {
this.$nextTick(() => {
const updateDynamicCellHeightTimeout = setTimeout(async () => {
for (let i = 0; i < list.length; i++) {
let item = list[i];
const cellNode = await this._getNodeClientRect(`#zp-${item[c.listCellIndexKey]}`,this.finalUseInnerList);
const hasCellNode = cellNode && cellNode.length;
const currentHeight = hasCellNode ? cellNode[0].height : 0;
if (!hasCellNode) {
clearTimeout(updateDynamicCellHeightTimeout);
this.virtualHeightCacheList = this.virtualHeightCacheList.slice(-i);
if (this.getCellHeightRetryCount.dynamic > 10) {
u.consoleErr('获取虚拟列表cell高度失败,可能是for循环cell处没有写:id="`zp-${item.zp_index}`",请检查您的代码!')
return;
}
this.getCellHeightRetryCount.dynamic++;
this._updateDynamicCellHeight(list);
break;
}
let lastHeightCache = null;
if (this.virtualHeightCacheList.length) {
lastHeightCache = this.virtualHeightCacheList.slice(-1)[0];
}
const lastHeight = lastHeightCache ? lastHeightCache.totalHeight : 0;
this.virtualHeightCacheList.push({
height: currentHeight,
lastHeight: lastHeight,
totalHeight: lastHeight + currentHeight
});
}
this._updateVirtualScroll(this.oldScrollTop);
}, c.delayTime)
})
},
//设置cellItem的index
_setCellIndex(list, isFirstPage) {
let lastItem = null;
let lastItemIndex = 0;
if (!isFirstPage) {
lastItemIndex = this.realTotalData.length;
if (this.realTotalData.length) {
lastItem = this.realTotalData.slice(-1)[0];
}
if (lastItem && lastItem[c.listCellIndexKey] !== undefined) {
lastItemIndex = lastItem[c.listCellIndexKey] + 1;
}
} else {
this._resetDynamicListState();
}
for (let i = 0; i < list.length; i++) {
let item = list[i];
if (!item || Object.prototype.toString.call(item) !== '[object Object]') {
item = {item};
}
item[c.listCellIndexKey] = lastItemIndex + i;
item[c.listCellIndexUniqueKey] = `${this.virtualListKey}-${item[c.listCellIndexKey]}`;
list[i] = item;
}
this.getCellHeightRetryCount.dynamic = 0;
this.cellHeightMode === Enum.CellHeightMode.Dynamic && this._updateDynamicCellHeight(list);
},
//更新scroll滚动
_updateVirtualScroll(scrollTop, scrollDiff = 0) {
const currentTimeStamp = u.getTime();
if (scrollTop === 0) {
this._resetTopRange();
}
if (scrollTop !== 0 && this.virtualScrollTimeStamp && currentTimeStamp - this.virtualScrollTimeStamp <= this.virtualScrollDisTimeStamp) {
return;
}
this.virtualScrollTimeStamp = Number(currentTimeStamp);
let scrollIndex = 0;
const cellHeightMode = this.cellHeightMode;
if (cellHeightMode === Enum.CellHeightMode.Fixed) {
scrollIndex = parseInt(scrollTop / this.virtualCellHeight) || 0;
this._updateFixedTopRangeIndex(scrollIndex);
this._updateFixedBottomRangeIndex(scrollIndex);
} else if(cellHeightMode === Enum.CellHeightMode.Dynamic) {
const scrollDirection = scrollDiff > 0 ? 'top' : 'bottom';
const rangePageHeight = this.virtualRangePageHeight;
const topRangePageOffset = scrollTop - rangePageHeight;
const bottomRangePageOffset = scrollTop + this.finalVirtualPageHeight + rangePageHeight;
let virtualBottomRangeIndex = 0;
let virtualPlaceholderBottomHeight = 0;
let reachedLimitBottom = false;
let lastHeightCache = null;
const heightCacheList = this.virtualHeightCacheList;
if (heightCacheList.length) {
lastHeightCache = heightCacheList.slice(-1)[0];
}
let startTopRangeIndex = this.virtualTopRangeIndex;
if (scrollDirection === 'bottom') {
for (let i = startTopRangeIndex; i < heightCacheList.length;i++){
const heightCacheItem = heightCacheList[i];
if (heightCacheItem && heightCacheItem.totalHeight > topRangePageOffset) {
this.virtualTopRangeIndex = i;
this.virtualPlaceholderTopHeight = heightCacheItem.lastHeight;
break;
}
}
} else {
let topRangeMatched = false;
for (let i = startTopRangeIndex; i >= 0;i--){
const heightCacheItem = heightCacheList[i];
if (heightCacheItem && heightCacheItem.totalHeight < topRangePageOffset) {
this.virtualTopRangeIndex = i;
this.virtualPlaceholderTopHeight = heightCacheItem.lastHeight;
topRangeMatched = true;
break;
}
}
!topRangeMatched && this._resetTopRange();
}
for (let i = this.virtualTopRangeIndex; i < heightCacheList.length;i++){
const heightCacheItem = heightCacheList[i];
if (heightCacheItem && heightCacheItem.totalHeight > bottomRangePageOffset) {
virtualBottomRangeIndex = i;
virtualPlaceholderBottomHeight = lastHeightCache.totalHeight - heightCacheItem.totalHeight;
reachedLimitBottom = true;
break;
}
}
if (!reachedLimitBottom || this.virtualBottomRangeIndex === 0) {
this.virtualBottomRangeIndex = this.realTotalData.length ? this.realTotalData.length - 1 : this.pageSize;
this.virtualPlaceholderBottomHeight = 0;
} else {
this.virtualBottomRangeIndex = virtualBottomRangeIndex;
this.virtualPlaceholderBottomHeight = virtualPlaceholderBottomHeight;
}
this._updateVirtualList();
}
},
//更新fixedCell模式下topRangeIndex&placeholderTopHeight
_updateFixedTopRangeIndex(scrollIndex) {
let virtualTopRangeIndex = this.virtualCellHeight === 0 ? 0 : scrollIndex - parseInt(this.finalVirtualPageHeight / this.virtualCellHeight) * this.preloadPage;
virtualTopRangeIndex *= this.virtualListCol;
virtualTopRangeIndex = Math.max(0, virtualTopRangeIndex);
this.virtualTopRangeIndex = virtualTopRangeIndex;
this.virtualPlaceholderTopHeight = (virtualTopRangeIndex / this.virtualListCol) * this.virtualCellHeight;
},
//更新fixedCell模式下bottomRangeIndex&placeholderBottomHeight
_updateFixedBottomRangeIndex(scrollIndex) {
let virtualBottomRangeIndex = this.virtualCellHeight === 0 ? this.pageSize : scrollIndex + parseInt(this.finalVirtualPageHeight / this.virtualCellHeight) * (this.preloadPage + 1);
virtualBottomRangeIndex *= this.virtualListCol;
virtualBottomRangeIndex = Math.min(this.realTotalData.length, virtualBottomRangeIndex);
this.virtualBottomRangeIndex = virtualBottomRangeIndex;
this.virtualPlaceholderBottomHeight = (this.realTotalData.length - virtualBottomRangeIndex) * this.virtualCellHeight / this.virtualListCol;
this._updateVirtualList();
},
//更新virtualList
_updateVirtualList() {
const shouldUpdateList = this.updateVirtualListFromDataChange || (this.lastVirtualTopRangeIndex !== this.virtualTopRangeIndex || this.lastVirtualBottomRangeIndex !== this.virtualBottomRangeIndex);
if (shouldUpdateList) {
this.updateVirtualListFromDataChange = false;
this.lastVirtualTopRangeIndex = this.virtualTopRangeIndex;
this.lastVirtualBottomRangeIndex = this.virtualBottomRangeIndex;
this.virtualList = this.realTotalData.slice(this.virtualTopRangeIndex, this.virtualBottomRangeIndex + 1);
}
},
//重置动态cell模式下的高度缓存数据、虚拟列表和滚动状态
_resetDynamicListState(resetVirtualList = false) {
this.virtualHeightCacheList = [];
if (resetVirtualList) {
this.virtualList = [];
}
this.virtualTopRangeIndex = 0;
this.virtualPlaceholderTopHeight = 0;
},
//重置topRangeIndex和placeholderTopHeight
_resetTopRange() {
this.virtualTopRangeIndex = 0;
this.virtualPlaceholderTopHeight = 0;
this._updateVirtualList();
},
//检测虚拟列表当前滚动位置,如发现滚动位置不正确则重新计算虚拟列表相关参数(为解决在App中可能出现的长时间进入后台后打开App白屏的问题)
_checkVirtualListScroll() {
if (this.finalUseVirtualList) {
this.$nextTick(() => {
this._getNodeClientRect('.zp-paging-touch-view').then(node => {
const hasNode = node && node.length;
const currentTop = hasNode ? node[0].top : 0;
if (!hasNode || (currentTop === this.pagingOrgTop && this.virtualPlaceholderTopHeight !== 0)){
this._updateVirtualScroll(0);
}
});
})
}
}
}
}
export default ZPVirtualList;

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